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

Flutter DraggableScrollableSheet键盘弹出时重置为minSize问题

问题原因
  • 软键盘弹出时会修改MediaQuery.viewInsets.bottom值,触发页面整体重绘,DraggableScrollableSheet默认会在重绘时基于新的可视区域重新计算尺寸,结合你开启的snap自动吸附逻辑,尺寸计算出现偏差后会自动回弹到最近的锚点也就是minChildSize位置。
  • 现有逻辑中通过NotificationListener获取的extent值会在布局重绘的中间态被更新,干扰了顶部占位容器的高度计算,进一步加剧布局错乱。
  • 没有对输入框聚焦、键盘弹出的场景做特殊处理,sheet状态没有被锁定,会响应默认的尺寸重算逻辑。
修复方案
  1. 新增输入框焦点监听,在键盘弹出时临时关闭snap吸附,主动将sheet展开到最大高度,避免回弹。
  2. 优化extent更新逻辑,键盘弹出状态下不接收布局重绘的中间态extent值,避免高度计算错乱。
  3. 适配键盘弹出时的页面高度,保证输入框不被遮挡。
  4. 键盘收起后恢复原有拖拽、吸附逻辑,不影响正常交互。
具体实现代码

首先在对应State类中新增必要的状态变量:

// 键盘弹出状态标记
bool _isKeyboardOpen = false;
// 记录键盘弹出前的sheet位置,键盘收起时可按需恢复
double _lastExtentBeforeKeyboardShow = 0;
// 输入框焦点监听节点
final FocusNode _inputFocusNode = FocusNode();
// 原有extent变量保留
late double extent;

在initState中添加焦点监听逻辑:

@override
void initState() {
  super.initState();
  extent = 0.175; // 初始值和initialChildSize保持一致
  _inputFocusNode.addListener(() {
    setState(() {
      _isKeyboardOpen = _inputFocusNode.hasFocus;
      if (_isKeyboardOpen) {
        _lastExtentBeforeKeyboardShow = extent;
      }
    });
    if (_isKeyboardOpen) {
      // 等待布局完成后执行展开动画,避免和键盘弹出动画冲突
      WidgetsBinding.instance.addPostFrameCallback((_) {
        widget.draggableScrollableController.animateTo(
          heightFactorForBottomNavScreen,
          duration: const Duration(milliseconds: 220),
          curve: Curves.easeOutCubic,
        );
      });
    }
  });
}

@override
void dispose() {
  _inputFocusNode.dispose();
  super.dispose();
}

修改原有布局代码,适配键盘弹出场景:

NotificationListener<DraggableScrollableNotification>(
  onNotification: (d) {
    // 键盘弹出时不更新extent,避免中间态值干扰布局计算
    if (!_isKeyboardOpen) {
      extent = d.extent;
    }
    return true;
  },
  child: DraggableScrollableSheet(
    initialChildSize: .175,
    minChildSize: .175,
    maxChildSize: heightFactorForBottomNavScreen,
    // 键盘弹出时临时关闭吸附,避免自动回弹
    snap: !_isKeyboardOpen,
    snapSizes: [.175, heightFactorForBottomNavScreen],
    controller: widget.draggableScrollableController,
    expand: false,
    builder: (BuildContext context, ScrollController scrollController) {
      double extentInPercent =
          (heightFactorForBottomNavScreen - extent) /
              heightFactorForBottomNavScreen;
      final ThemeData theme = Theme.of(context);
      return Theme(
        data: theme.copyWith(
            colorScheme: theme.colorScheme
                .copyWith(secondary: Colors.transparent)),
        child: SingleChildScrollView(
          physics: const ClampingScrollPhysics(),
          controller: scrollController,
          child: Column(
            children: [
              Container(
                height: 170 * extentInPercent,
                color: Colors.white38,
                child: LayoutBuilder(builder: (_, size) {
                  double width = size.maxWidth / _noOfPages;
                  return Row(
                    mainAxisSize: MainAxisSize.min,
                    children: List.generate(_noOfPages, (index) {
                      double sIndex = widget.selectedIndex - index;
                      bool selected = sIndex.abs() <= 0.5;
                      return GestureDetector(
                        behavior: HitTestBehavior.translucent,
                        onTap: () {},
                        child: Container(
                          color: Colors.transparent,
                          width: width,
                          height: 120,
                        ),
                      );
                    }),
                  );
                }),
              ),
              // 将焦点节点传入承载输入框的模块,绑定到TextField的focusNode属性上
              NavigateHome(focusNode: _inputFocusNode)
            ],
          ),
        ),
      );
    },
  ),
)
注意事项

请确认当前页面的Scaffold没有设置resizeToAvoidBottomInset: false,保持默认值为true即可让页面在键盘弹出时自动调整可视区域,避免输入框被键盘遮挡。如果业务场景必须关闭该属性,需要在计算heightFactorForBottomNavScreen时手动扣除MediaQuery.of(context).viewInsets.bottom对应的键盘高度,保证展开位置计算准确。

如果不需要键盘收起后回到之前的sheet位置,可以删除_lastExtentBeforeKeyboardShow相关逻辑,保持sheet在输入完成后停留在最大展开位置即可,符合多数同类交互的用户预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 15:27:17