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

Flutter中showModalBottomSheet与键盘重叠问题求助

解决Flutter showModalBottomSheet被键盘遮挡的问题

核心问题分析

你遇到的问题根源在于:

  1. 使用FractionallySizedBox固定了modal的高度因子(heightFactor: 0.4),导致即使添加了viewInsets.bottom的padding,modal整体高度被限制,内容无法随键盘弹出而向上调整
  2. LoginPage中嵌套的SafeArea会额外添加底部内边距,和你手动设置的viewInsets.bottom冲突,抵消了键盘适配效果

具体修改方案

方案1:简化Modal结构,移除固定高度限制

修改_showLoginModal方法,去掉FractionallySizedBox,直接用Container设置基础高度,并通过padding适配键盘:

void _showLoginModal(BuildContext context) {
  showModalBottomSheet(
    context: context,
    isScrollControlled: true,
    isDismissible: false,
    enableDrag: false,
    backgroundColor: Colors.transparent,
    builder: (BuildContext context) {
      return WillPopScope(
        onWillPop: () async => false,
        child: Container(
          // 保留原有的40%屏幕高度作为基础
          height: MediaQuery.of(context).size.height * 0.4,
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(20),
              topRight: Radius.circular(20),
            ),
          ),
          // 底部padding自动适配键盘高度
          padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
          child: LoginPage(),
        ),
      );
    },
  );
}

方案2:修改LoginPage,移除多余的SafeArea

LoginPage中的SafeArea是多余的,因为modal本身已经处于屏幕底部区域,保留它会导致内边距冲突,修改后的代码:

@override
Widget build(BuildContext context) {
  return SingleChildScrollView(
    padding: EdgeInsets.all(10),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        SizedBox(height: 20),
        Row(
          children: [
            Expanded(
              child: TextField(
                controller: firstNameController,
                decoration: _buildInputDecoration('First Name'),
                textCapitalization: TextCapitalization.words,
              ),
            ),
            SizedBox(width: 20),
            Expanded(
              child: TextField(
                controller: lastNameController,
                decoration: _buildInputDecoration('Last Name'),
                textCapitalization: TextCapitalization.words,
              ),
            ),
          ],
        ),
        SizedBox(height: 10),
        TextField(
          controller: phoneNumberController,
          decoration: _buildInputDecoration('10 digit Mobile Number'),
          keyboardType: TextInputType.phone,
          maxLength: 10,
          enabled: !isOtpSent,
        ),
        SizedBox(height: 10),
        if (isOtpSent)
          TextField(
            controller: otpController,
            decoration: _buildInputDecoration('Enter OTP'),
            keyboardType: TextInputType.number,
          ),
        SizedBox(height: 10),
        ElevatedButton.icon(
          onPressed: () {
            if (isOtpSent) {
              verifyOtp();
            } else {
              sendOtp();
            }
          },
          style: ButtonStyle(
            backgroundColor:
                MaterialStateProperty.all<Color>(Color(0xFF0194FE)),
            shape: MaterialStateProperty.all<RoundedRectangleBorder>(
              RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(10),
              ),
            ),
          ),
          icon: Icon(Icons.send, color: Colors.white),
          label: Text(
            isOtpSent ? 'Verify OTP' : 'Send OTP',
            style: TextStyle(color: Colors.white),
          ),
        ),
        // 可选:添加空白区域确保按钮不会被键盘边缘遮挡
        SizedBox(height: MediaQuery.of(context).viewInsets.bottom > 0 ? 20 : 0),
      ],
    ),
  );
}

方案3:修复你参考Andrei帖子后的代码

你之前修改的showSoftKeyboardAwareBottomModal中,错误地同时使用了FractionallySizedBox固定高度和手动计算modalHeight,导致内容被压缩。修改为:

Future<void> showSoftKeyboardAwareBottomModal(BuildContext context) async {
  await showModalBottomSheet<void>(
    context: context,
    isScrollControlled: true,
    backgroundColor: Colors.transparent,
    builder: (context) {
      final viewInsets = MediaQuery.of(context).viewInsets;
      final screenSize = MediaQuery.of(context).size;
      return WillPopScope(
        onWillPop: () async => false,
        child: Container(
          // 基础高度 + 键盘高度,确保modal能完整显示内容
          height: screenSize.height * 0.4 + viewInsets.bottom,
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(20),
              topRight: Radius.circular(20),
            ),
          ),
          padding: EdgeInsets.fromLTRB(16, 16, 16, viewInsets.bottom),
          child: LoginPage(),
        ),
      );
    },
  );
}

关键注意点

  • 必须设置isScrollControlled: true,这是modal能够响应键盘高度变化的核心开关
  • 避免使用固定高度的布局组件(如FractionallySizedBox)限制modal整体高度,或者要动态将键盘高度加入总高度
  • 移除modal内容中多余的SafeArea,防止内边距冲突
  • 确保内容被SingleChildScrollView包裹,这样键盘弹出时内容可以滚动避免被遮挡

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:54:52