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

Flutter中BottomSheet内ListView的TextField被键盘遮挡问题求助

解决BottomSheet内ListView的TextField被键盘遮挡问题

方案1:利用FocusNode+ScrollController精准滚动到聚焦项

核心思路是给每个TextField绑定FocusNode,监听焦点获取事件,一旦某个TextField获得焦点,就通过滚动控制将其移动到ListView的可见区域。如果item高度不固定,推荐用Scrollable.ensureVisible自动计算位置,适配性更强。

代码示例(适配任意item高度)

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

  @override
  State<SheetWithTextFieldList> createState() => _SheetWithTextFieldListState();
}

class _SheetWithTextFieldListState extends State<SheetWithTextFieldList> {
  final ScrollController _scrollController = ScrollController();
  late List<FocusNode> _focusNodes;
  late List<GlobalKey> _itemKeys;

  @override
  void initState() {
    super.initState();
    // 初始化对应数量的FocusNode和GlobalKey
    const itemCount = 15;
    _focusNodes = List.generate(itemCount, (_) => FocusNode());
    _itemKeys = List.generate(itemCount, (_) => GlobalKey());

    // 给每个FocusNode添加焦点监听
    for (int i = 0; i < itemCount; i++) {
      _focusNodes[i].addListener(() {
        if (_focusNodes[i].hasFocus) {
          final itemContext = _itemKeys[i].currentContext;
          if (itemContext != null) {
            // 自动滚动到聚焦项,设置居中避免被键盘遮挡
            Scrollable.ensureVisible(
              itemContext,
              duration: const Duration(milliseconds: 300),
              curve: Curves.easeInOut,
              alignment: 0.5,
            );
          }
        }
      });
    }
  }

  @override
  void dispose() {
    _scrollController.dispose();
    // 销毁所有FocusNode避免内存泄漏
    for (final node in _focusNodes) {
      node.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      // 固定BottomSheet高度,避免被键盘顶满屏幕
      height: MediaQuery.of(context).size.height * 0.7,
      padding: const EdgeInsets.all(16),
      child: ListView.builder(
        controller: _scrollController,
        itemCount: 15,
        itemBuilder: (context, index) {
          return Padding(
            key: _itemKeys[index],
            padding: const EdgeInsets.symmetric(vertical: 8),
            child: TextField(
              focusNode: _focusNodes[index],
              decoration: InputDecoration(
                hintText: '输入框 ${index + 1}',
                border: const OutlineInputBorder(),
              ),
            ),
          );
        },
      ),
    );
  }
}

为什么不能用resizeToAvoidBottomInset?

给BottomSheet嵌套Scaffold并设置resizeToAvoidBottomInset: true会让BottomSheet直接占据整个屏幕,完全破坏了BottomSheet的模态弹窗特性,因此这种方案不可行。

注意事项

  • 必须在dispose方法中销毁FocusNode和ScrollController,防止内存泄漏。
  • Scrollable.ensureVisible会自动适配item高度变化,比固定计算滚动位置更可靠。
  • 可通过调整alignment参数控制滚动后的位置(0为顶部对齐,1为底部对齐,0.5为居中)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 19:01:10