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

Flutter:键盘弹出时如何让Widget(2)自动占满屏幕并隐藏Widget(1)

Flutter 键盘弹出时自动调整组件占比需求

需求:键盘弹出时,强制让下方表单区域(Widget(2))填充几乎整个屏幕,隐藏上方欢迎文本区域(Widget(1))。目前手动将Widget(2)的flex改为9能达到预期效果,但需要实现键盘弹出/收起时自动切换该比例。

组件代码

class RegistrationForm extends StatelessWidget {
  const RegistrationForm({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: true,
      body: Container(
        color: Colors.green,
        child: Form(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              Flexible(
                flex: 1,
                child: Container(
                  padding: const EdgeInsets.only(
                    top: 60,
                    left: 30,
                    right: 30,
                    bottom: 5,
                  ),
                  height: 240,
                  child: const Text(
                    "Welcome, please fill your data",
                    style: TextStyle(fontSize: 35),
                  ),
                ),
              ),
              Flexible(
                flex: 3,
                child: SingleChildScrollView(
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 35.0,
                      vertical: 50,
                    ),
                    decoration: const BoxDecoration(
                        color: Colors.black,
                        borderRadius: BorderRadius.only(
                          topLeft: Radius.circular(40),
                          topRight: Radius.circular(40),
                        )),
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: [
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration: InputDecoration(labelText: "Name"),
                          ),
                        ),
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration: InputDecoration(labelText: "Email"),
                          ),
                        ),
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration: InputDecoration(labelText: "Password"),
                          ),
                        ),
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration:
                                InputDecoration(labelText: "Repeat password"),
                          ),
                        ),
                        Padding(
                          padding: EdgeInsets.symmetric(vertical: 40),
                          child: ElevatedButton(
                            child: Text(
                              "Register",
                              style: TextStyle(color: Colors.green),
                            ),
                            onPressed: () => print("register"),
                          ),
                        )
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

行为对比

无键盘时默认效果

无键盘时默认效果

当前键盘弹出时效果

当前键盘弹出时效果

预期效果

预期效果

实现方案

要实现自动切换,核心是监听键盘弹出/收起状态,动态调整两个Flexible的flex值,需将组件改为StatefulWidget维护状态:

修改后完整代码

class RegistrationForm extends StatefulWidget {
  const RegistrationForm({super.key});

  @override
  State<RegistrationForm> createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State<RegistrationForm> with WidgetsBindingObserver {
  // 默认无键盘时的flex比例
  int _topFlex = 1;
  int _bottomFlex = 3;

  @override
  void initState() {
    super.initState();
    // 添加键盘监听
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    // 移除监听避免内存泄漏
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    // 通过屏幕底部内边距判断键盘状态
    final bottomInset = WidgetsBinding.instance.window.viewInsets.bottom;
    setState(() {
      if (bottomInset > 0) {
        // 键盘弹出,调整比例让表单区域占满屏幕
        _topFlex = 1;
        _bottomFlex = 9;
        // 若需完全隐藏上方区域,可设置_topFlex=0
        // _topFlex = 0;
        // _bottomFlex = 1;
      } else {
        // 键盘收起,恢复默认比例
        _topFlex = 1;
        _bottomFlex = 3;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: true,
      body: Container(
        color: Colors.green,
        child: Form(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              Flexible(
                flex: _topFlex,
                // 可选:当topFlex为0时完全隐藏组件
                child: Visibility(
                  visible: _topFlex != 0,
                  child: Container(
                    padding: const EdgeInsets.only(
                      top: 60,
                      left: 30,
                      right: 30,
                      bottom: 5,
                    ),
                    height: 240,
                    child: const Text(
                      "Welcome, please fill your data",
                      style: TextStyle(fontSize: 35),
                    ),
                  ),
                ),
              ),
              Flexible(
                flex: _bottomFlex,
                child: SingleChildScrollView(
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 35.0,
                      vertical: 50,
                    ),
                    decoration: const BoxDecoration(
                        color: Colors.black,
                        borderRadius: BorderRadius.only(
                          topLeft: Radius.circular(40),
                          topRight: Radius.circular(40),
                        )),
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: [
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration: InputDecoration(labelText: "Name"),
                          ),
                        ),
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration: InputDecoration(labelText: "Email"),
                          ),
                        ),
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration: InputDecoration(labelText: "Password"),
                          ),
                        ),
                        const Padding(
                          padding: EdgeInsets.symmetric(vertical: 20),
                          child: TextField(
                            decoration:
                                InputDecoration(labelText: "Repeat password"),
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.symmetric(vertical: 40),
                          child: ElevatedButton(
                            child: const Text(
                              "Register",
                              style: TextStyle(color: Colors.green),
                            ),
                            onPressed: () => print("register"),
                          ),
                        )
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

方案说明

  • 通过WidgetsBindingObserver的didChangeMetrics方法监听屏幕尺寸变化,利用viewInsets.bottom判断键盘是否弹出(值大于0则键盘显示)。
  • 动态更新两个Flexible的flex值,配合Visibility组件可完全隐藏上方区域,或保留小比例空间。
  • 务必在dispose中移除监听,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 20:44:50