You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter表单页面:TextField获焦弹出键盘时如何滚动屏幕

Flutter表单键盘弹出时自动滚动到输入框的解决方案

方法一:使用SingleChildScrollView + resizeToAvoidBottomInset

这是最基础且高效的方案,适合大多数普通表单场景。通过将表单包裹在SingleChildScrollView中,并利用resizeToAvoidBottomInset属性自动调整布局,避免输入框被键盘遮挡。

class UserInfoForm extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('用户信息收集')),
      body: SingleChildScrollView(
        // 自动调整页面高度,避免键盘遮挡输入框
        resizeToAvoidBottomInset: true,
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: const [
            TextField(decoration: InputDecoration(labelText: '姓名')),
            SizedBox(height: 16),
            TextField(decoration: InputDecoration(labelText: '手机号')),
            SizedBox(height: 16),
            TextField(decoration: InputDecoration(labelText: '邮箱')),
            SizedBox(height: 16),
            TextField(decoration: InputDecoration(labelText: '详细地址')),
            // 添加更多表单字段
          ],
        ),
      ),
    );
  }
}

说明:resizeToAvoidBottomInset默认值为true,但需确保SingleChildScrollView是Scaffold.body的直接子组件,否则可能无法正常生效。


方法二:手动监听焦点,精准控制滚动

如果表单过长或需要自定义滚动动画,可通过FocusNode监听输入框焦点变化,调用Scrollable.ensureVisible()实现精准滚动。

class UserInfoForm extends StatefulWidget {
  @override
  State<UserInfoForm> createState() => _UserInfoFormState();
}

class _UserInfoFormState extends State<UserInfoForm> {
  late FocusNode _nameFocus;
  late FocusNode _addressFocus;
  final GlobalKey _nameFieldKey = GlobalKey();
  final GlobalKey _addressFieldKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    // 为输入框创建焦点监听
    _nameFocus = FocusNode()..addListener(_handleFocusChange);
    _addressFocus = FocusNode()..addListener(_handleFocusChange);
  }

  void _handleFocusChange() {
    // 当输入框获取焦点时,滚动到对应位置
    if (_nameFocus.hasFocus) {
      _scrollToField(_nameFieldKey);
    } else if (_addressFocus.hasFocus) {
      _scrollToField(_addressFieldKey);
    }
  }

  void _scrollToField(GlobalKey key) {
    final context = key.currentContext;
    if (context != null) {
      Scrollable.ensureVisible(
        context,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        alignment: 0.3, // 调整输入框在屏幕中的垂直位置
      );
    }
  }

  @override
  void dispose() {
    // 释放焦点资源,避免内存泄漏
    _nameFocus.dispose();
    _addressFocus.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('用户信息收集')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TextField(
              key: _nameFieldKey,
              focusNode: _nameFocus,
              decoration: const InputDecoration(labelText: '姓名'),
            ),
            const SizedBox(height: 16),
            // 省略中间字段...
            const SizedBox(height: 400), // 模拟长表单
            TextField(
              key: _addressFieldKey,
              focusNode: _addressFocus,
              decoration: const InputDecoration(labelText: '详细地址'),
              maxLines: 3,
            ),
          ],
        ),
      ),
    );
  }
}

注意事项:

  • 必须在StatefulWidget中管理FocusNode,并在dispose方法中释放资源
  • alignment参数可以调整输入框滚动后的垂直位置,0表示顶部对齐,1表示底部对齐

内容的提问来源于stack exchange,提问作者Deva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 16:38:41