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

Flutter动态增删TextFormField异常:删指定项却删最后一项

问题根源与解决方案

核心问题

  1. 自定义GTextField未绑定控制器:你的TextFormField没有接收传入的controller参数,导致输入框和控制器完全脱节,这是基础错误。
  2. Widget复用导致删除错位:使用for (var ctrl in controllers)遍历生成输入框,且未给每个GTextField设置唯一key,Flutter会复用现有Widget,删除时出现错位。
  3. 删除逻辑顺序错误:先调用setState再修改控制器列表,此时UI重建时列表还没更新,导致删除逻辑失效。

修复步骤

1. 修复自定义GTextField

给TextFormField添加controller参数绑定:

import 'package:flutter/material.dart';

class GTextField extends StatelessWidget {
  final Widget? prefixIcon;
  final Widget? suffixIcon;
  final String hintText;
  final TextEditingController controller;
  const GTextField({
    super.key,
    this.prefixIcon,
    this.suffixIcon,
    required this.hintText,
    required this.controller,
  });

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      controller: controller, // 新增:绑定控制器
      decoration: InputDecoration(
        hintText: hintText,
        contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
        prefixIcon: prefixIcon,
        prefixIconConstraints: const BoxConstraints(),
        suffixIcon: suffixIcon,
        filled: true,
        fillColor: Colors.white10.withOpacity(0.05),
        border: OutlineInputBorder(
          borderSide: BorderSide.none,
          borderRadius: BorderRadius.circular(30),
        ),
        errorBorder: OutlineInputBorder(
          borderSide: const BorderSide(color: Colors.red),
          borderRadius: BorderRadius.circular(30),
        ),
      ),
    );
  }
}

2. 修改页面代码的遍历与删除逻辑

  • 改用索引遍历生成输入框,避免indexOf的潜在问题;
  • 给每个GTextField设置唯一key,防止Widget复用错位;
  • 调整删除逻辑顺序,先修改列表再调用setState:
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gdsc_session2/constants/image_strings.dart';

import '../widgets/text_field.dart';

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

  @override
  State<NewTask> createState() => _NewTaskState();
}

class _NewTaskState extends State<NewTask> {
  final GlobalKey<FormState> formKey = GlobalKey();
  final List<TextEditingController> controllers = [];
  
  @override
  void dispose() {
    // 新增:销毁所有控制器,避免内存泄漏
    for (var ctrl in controllers) {
      ctrl.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(
            child: Form(
              key: formKey,
              child: ListView(
                children: [
                  // 改用索引遍历
                  for (int i = 0; i < controllers.length; i++) ...[
                    GTextField(
                      key: ValueKey(controllers[i]), // 设置唯一key
                      hintText: 'Todo ${i + 1}',
                      prefixIcon: Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 20),
                        child: SvgPicture.asset(
                          GImageStrings.clipboard,
                          width: 25,
                        ),
                      ),
                      suffixIcon: IconButton(
                          onPressed: () {
                            setState(() {
                              removeTextField(i); // 先修改列表,再触发重建
                            });
                          },
                          icon: const Icon(Icons.clear)),
                      controller: controllers[i],
                    ),
                    const SizedBox(
                      height: 15,
                    ),
                  ],

                  // Add Button
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      SizedBox(
                        width: 200,
                        child: ElevatedButton(
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.blue.withOpacity(0.2),
                            ),
                            onPressed: () {
                              setState(() {
                                addTextField();
                              });
                            },
                            child: const Row(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Icon(Icons.add_circle),
                                SizedBox(
                                  width: 10,
                                ),
                                Text(
                                  'Add',
                                  style: TextStyle(color: Colors.white),
                                )
                              ],
                            )),
                      ),
                    ],
                  )
                ],
              ),
            ),
          )
        ],
      ),
    );
  }

  void addTextField() {
    controllers.add(TextEditingController());
  }

  void removeTextField(int index) {
    controllers[index].dispose(); // 销毁要删除的控制器
    controllers.removeAt(index);
  }
}

额外优化

  • 新增了dispose方法,遍历销毁所有控制器,避免内存泄漏;
  • 删除单个控制器时先调用dispose,再从列表移除,进一步避免内存问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 17:53:28