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

Flutter Android/iOS:带TextInputAction.next的动态列表行为异常

解决Flutter动态列表中最后一个TextFormField点击Next/Done时键盘意外关闭的问题

问题根源

  1. 最后一个输入框设置了TextInputAction.done,系统默认点击Done按钮会关闭键盘,不符合保持键盘打开的需求
  2. onTapOutside中的全局失焦逻辑,在焦点切换过程中可能误触发导致键盘关闭
  3. 嵌套滚动结构(SingleChildScrollView包裹ListView)在焦点切换时的布局重绘,可能干扰键盘状态

通用解决方案

以下是适配所有设备的解决步骤:

1. 管理每个输入框的焦点节点

为每个TextFormField创建独立的FocusNode,手动控制焦点切换逻辑,避免依赖系统默认的Done行为。

2. 统一焦点切换逻辑

在onFieldSubmitted中处理焦点跳转:

  • 非最后一个输入框:切换到下一个输入框的焦点
  • 最后一个输入框:保持当前焦点,不触发键盘关闭

3. 调整外部点击失焦逻辑

避免全局强制失焦,改为仅在必要时处理,防止焦点切换时误触发。

修改后的完整代码

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: const ItemList(),
    );
  }
}

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

  @override
  State<ItemList> createState() => _ItemListState();
}

class _ItemListState extends State<ItemList> {
  final _formKey = GlobalKey<FormState>();
  final List<String> items = [];
  late List<FocusNode> _focusNodes;

  @override
  void initState() {
    items.addAll(List<String>.generate(20, (int index) => 'String$index', growable: true));
    // 初始化焦点节点列表
    _focusNodes = List.generate(items.length, (index) => FocusNode());
    super.initState();
  }

  @override
  void dispose() {
    // 清理焦点节点,避免内存泄漏
    for (var node in _focusNodes) {
      node.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: SingleChildScrollView(
            child: Column(
              children: [
                const Row(
                  children: [
                    Text('Title: '),
                    Text('Value'),
                  ],
                ),
                Form(
                  key: _formKey,
                  child: ListView.builder(
                    scrollDirection: Axis.vertical,
                    shrinkWrap: true,
                    physics: const NeverScrollableScrollPhysics(),
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                      final item = items[index];
                      final bool isLast = items.length - 1 == index;
                      return Row(
                        children: [
                          Text(item),
                          const SizedBox(width: 20),
                          Expanded(
                            child: TextFormField(
                              focusNode: _focusNodes[index],
                              // 所有输入框统一用next,手动处理最后一个的逻辑
                              textInputAction: TextInputAction.next,
                              onFieldSubmitted: (_) {
                                if (!isLast) {
                                  // 切换到下一个输入框焦点
                                  FocusScope.of(context).requestFocus(_focusNodes[index + 1]);
                                } else {
                                  // 最后一个输入框,保持焦点,不关闭键盘
                                  _focusNodes[index].requestFocus();
                                }
                              },
                              // 调整外部点击失焦逻辑,可根据需求选择是否保留
                              onTapOutside: (event) {
                                // 若需要点击外部关闭键盘则保留,否则注释
                                // FocusManager.instance.primaryFocus?.unfocus();
                              },
                              decoration: const InputDecoration(
                                isDense: true,
                              ),
                            ),
                          ),
                        ],
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        ),
        Align(
          alignment: Alignment.bottomLeft,
          child: Padding(
            padding: const EdgeInsets.all(15.0),
            child: ElevatedButton(
              onPressed: () {},
              child: const SizedBox(
                height: 40,
                child: Center(child: Text('Finish')),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

关键修改说明

  • 焦点节点管理:创建与输入框数量对应的FocusNode列表,确保每个输入框有独立焦点控制
  • 统一TextInputAction:所有输入框使用TextInputAction.next,避免系统默认的Done关闭键盘行为
  • 自定义焦点切换:在onFieldSubmitted中手动处理焦点跳转,最后一个输入框主动保持焦点,防止键盘关闭
  • 优化onTapOutside:可根据需求调整是否保留外部点击失焦逻辑,避免干扰焦点切换过程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 07:42:51