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

Flutter中ListView.builder内TextField提交后滚动至下一项的实现

实现ListView.builder中TextField提交后自动聚焦到下一个输入框

没问题,这就教你怎么用FocusNode在ListView.builder里实现提交后自动跳转到下一个TextField!核心思路是给每个TextField绑定独立的FocusNode,提交时切换焦点并滚动到对应位置,咱们一步步来:

核心步骤与完整代码示例

场景1:动态新增输入框(提交一个就加一个新的)

这种场景适合需要用户不断输入新值的情况,每提交一个数值就新增一个TextField并自动聚焦:

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

  @override
  State<DynamicTextFieldList> createState() => _DynamicTextFieldListState();
}

class _DynamicTextFieldListState extends State<DynamicTextFieldList> {
  // 存储用户输入的值
  List<String> valueList = [];
  // 存储每个TextField对应的FocusNode
  List<FocusNode> focusNodes = [];
  // 用于控制列表滚动
  final ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    // 初始化第一个FocusNode,默认显示第一个输入框
    focusNodes.add(FocusNode());
  }

  @override
  void dispose() {
    // 销毁所有FocusNode和滚动控制器,避免内存泄漏
    for (var node in focusNodes) {
      node.dispose();
    }
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        controller: _scrollController,
        // 多一个输入框用于下一次输入
        itemCount: valueList.length + 1,
        itemBuilder: (context, index) {
          // 按需创建新的FocusNode
          if (index == focusNodes.length) {
            focusNodes.add(FocusNode());
          }

          return Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              // 绑定当前index对应的FocusNode
              focusNode: focusNodes[index],
              keyboardType: TextInputType.number,
              onSubmitted: (value) {
                if (value.trim().isNotEmpty) {
                  setState(() {
                    valueList.add(value.trim());
                  });
                  // 聚焦到下一个输入框
                  if (index + 1 < focusNodes.length) {
                    FocusScope.of(context).requestFocus(focusNodes[index + 1]);
                    // 平滑滚动到列表底部,确保新输入框可见
                    _scrollController.animateTo(
                      _scrollController.position.maxScrollExtent,
                      duration: const Duration(milliseconds: 300),
                      curve: Curves.easeOut,
                    );
                  }
                }
              },
              decoration: InputDecoration(
                hintText: '输入第 ${index + 1} 个数值',
                border: const OutlineInputBorder(),
              ),
            ),
          );
        },
      ),
    );
  }
}

场景2:固定数量的输入框

如果是预设好数量的输入框(比如5个),提交当前输入框后自动聚焦到下一个,最后一个提交后收起键盘:

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

  @override
  State<FixedTextFieldList> createState() => _FixedTextFieldListState();
}

class _FixedTextFieldListState extends State<FixedTextFieldList> {
  static const int totalInputFields = 5;
  List<String> valueList = List.generate(totalInputFields, (_) => '');
  List<FocusNode> focusNodes = List.generate(totalInputFields, (_) => FocusNode());
  final ScrollController _scrollController = ScrollController();

  @override
  void dispose() {
    for (var node in focusNodes) {
      node.dispose();
    }
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        controller: _scrollController,
        itemCount: totalInputFields,
        itemBuilder: (context, index) {
          return Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              focusNode: focusNodes[index],
              keyboardType: TextInputType.number,
              onSubmitted: (value) {
                setState(() {
                  valueList[index] = value.trim();
                });
                // 不是最后一个输入框,就聚焦下一个
                if (index < totalInputFields - 1) {
                  FocusScope.of(context).requestFocus(focusNodes[index + 1]);
                  // 滚动到下一个输入框位置(这里估算每个item高度为80,也可以用GlobalKey获取准确位置)
                  _scrollController.animateTo(
                    (index + 1) * 80.0,
                    duration: const Duration(milliseconds: 300),
                    curve: Curves.easeOut,
                  );
                } else {
                  // 最后一个提交后收起键盘
                  FocusScope.of(context).unfocus();
                }
              },
              decoration: InputDecoration(
                hintText: '输入第 ${index + 1} 个数值',
                border: const OutlineInputBorder(),
              ),
            ),
          );
        },
      ),
    );
  }
}

关键知识点解析

  • FocusNode管理:每个TextField必须绑定独立的FocusNode,这样才能精准控制焦点切换;记得在dispose中销毁所有FocusNode,避免内存泄漏。
  • 焦点切换:使用FocusScope.of(context).requestFocus(nextFocusNode)可以直接将焦点转移到目标输入框。
  • 滚动控制:通过ScrollController的animateTo方法实现平滑滚动,确保下一个输入框出现在视野中,提升用户体验。

内容的提问来源于stack exchange,提问作者MD Solaiman Hossain Rakib

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:47:43