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
相关产品推荐
相关产品推荐

