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

Flutter实现键盘弹出时隐藏Bottom Sheet的方法求助

Flutter 实现键盘弹出时隐藏BottomSheet方案

核心思路

通过状态变量控制BottomSheet的显示/隐藏,结合焦点监听或全局键盘状态监听,在输入框获焦、键盘弹出时隐藏BottomSheet,失去焦点、键盘收起时恢复显示,同时保留BottomSheet内动态可滚动列表的特性。

具体实现步骤

  1. 转为StatefulWidget维护状态
    在页面的State类中添加布尔变量,用于控制BottomSheet的显示状态:
bool _showBottomSheet = true;
  1. 动态控制BottomSheet显示
    修改Scaffold的bottomSheet属性,根据状态变量返回对应组件或null:
bottomSheet: _showBottomSheet ? Container(
  // 你原有的BottomSheet布局代码
  decoration: BoxDecoration(
    color: mainColor,
    borderRadius: BorderRadius.only(
      topLeft: Radius.circular(16),
      topRight: Radius.circular(16),
    ),
  ),
  child: Container(
    margin: const EdgeInsetsDirectional.only(start: 2, end: 2, top: 2),
    padding: EdgeInsets.all(15),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(14),
        topRight: Radius.circular(14),
      ),
    ),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        // 替换为你的动态可滚动商品列表
        Expanded(
          child: ListView.builder(
            shrinkWrap: true,
            itemCount: 10,
            itemBuilder: (context, index) => Text("商品 $index"),
          ),
        ),
      ],
    ),
  ),
) : null,
  1. 监听输入框焦点变化(单输入场景)
    给输入框包裹FocusListener,在焦点切换时更新状态:
FocusListener(
  onFocusChange: (hasFocus) {
    setState(() {
      _showBottomSheet = !hasFocus;
    });
  },
  child: TextField(
    decoration: InputDecoration(hintText: "输入内容"),
  ),
)
  1. 全局键盘状态监听(多输入场景)
    通过WidgetsBindingObserver监听系统键盘状态,适合页面存在多个输入框的场景:
  • 在State类混入WidgetsBindingObserver:
class _YourPageState extends State<YourPage> with WidgetsBindingObserver {
  • 注册/取消监听:
@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(() {
    _showBottomSheet = bottomInset == 0;
  });
}

完整代码示例

class YourPage extends StatefulWidget {
  @override
  _YourPageState createState() => _YourPageState();
}

class _YourPageState extends State<YourPage> with WidgetsBindingObserver {
  bool _showBottomSheet = true;

  @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(() {
      _showBottomSheet = bottomInset == 0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      backgroundColor: Colors.white,
      bottomSheet: _showBottomSheet ? Container(
        decoration: BoxDecoration(
          color: mainColor,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(16),
            topRight: Radius.circular(16),
          ),
        ),
        child: Container(
          margin: const EdgeInsetsDirectional.only(start: 2, end: 2, top: 2),
          padding: EdgeInsets.all(15),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(14),
              topRight: Radius.circular(14),
            ),
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Expanded(
                child: ListView.builder(
                  shrinkWrap: true,
                  itemCount: 10,
                  itemBuilder: (context, index) => Text("商品 $index"),
                ),
              ),
            ],
          ),
        ),
      ) : null,
      body: Padding(
        padding: EdgeInsets.only(left: 12, top: 5, right: 12),
        child: SingleChildScrollView(
          child: Container(
            height: ScreenSizeConfig.screenHeight,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                TextField(
                  decoration: InputDecoration(hintText: "输入内容"),
                ),
                // 其他页面组件
              ],
            ),
          ),
        ),
      ),
    );
  }
}

替代方案说明

若需更复杂的BottomSheet交互,可使用PersistentBottomSheet:通过ScaffoldState.showBottomSheet创建并获取控制器,在键盘弹出时调用controller.close(),键盘收起时重新调用showBottomSheet打开,核心逻辑依然是通过状态触发显示/隐藏操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:06:44