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

ListView.builder中双TextField的增删功能异常排查请求

问题排查与修复:Flutter列表项添加/删除异常

存在的问题

  • 删除操作会误删其他列表项
  • 第二次点击“Add Shift”添加新项时,首次创建的空白输入框项会被覆盖

问题原因分析

  1. 重复键导致覆盖:添加新项时使用空字符串''作为Map的键,而Map的键具有唯一性,多次添加空键会直接覆盖之前的空键项,导致列表显示异常。
  2. 控制器生命周期错误:在ListView.builder的itemBuilder中每次创建TextEditingController,组件重建时会重新初始化控制器,导致输入的内容丢失,并且无法正确关联到对应的Map项。
  3. 删除逻辑依赖动态输入值:删除操作使用输入框当前的文本作为键,而非列表项对应的原始键,当用户修改输入框的键文本后,删除的目标会偏离预期,造成误删。

修复方案与代码实现

  1. 新增项时生成唯一键(例如使用时间戳或自增ID),避免键重复。
  2. 在状态类中维护控制器的Map,将每个列表项的键与对应的TextEditingController绑定,确保输入状态持久化。
  3. 删除操作直接使用列表项的原始键,保证删除目标准确。

修复后的完整代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Shift List'),
        ),
        body: ShiftList(),
      ),
    );
  }
}

class ShiftList extends StatefulWidget {
  @override
  _ShiftListState createState() => _ShiftListState();
}

class _ShiftListState extends State<ShiftList> {
  Map<String, String> shifts = {
    '1': 'Morning',
    '2': 'Afternoon',
    '3': 'Night',
  };
  // 维护每个键对应的控制器
  Map<String, TextEditingController> keyControllers = {};
  Map<String, TextEditingController> valueControllers = {};

  @override
  void initState() {
    super.initState();
    // 初始化现有项的控制器
    shifts.forEach((key, value) {
      keyControllers[key] = TextEditingController(text: key);
      valueControllers[key] = TextEditingController(text: value);
    });
  }

  @override
  void dispose() {
    // 销毁所有控制器,避免内存泄漏
    keyControllers.values.forEach((controller) => controller.dispose());
    valueControllers.values.forEach((controller) => controller.dispose());
    super.dispose();
  }

  void addShift() {
    setState(() {
      // 生成唯一键,这里用当前时间戳转字符串
      String newKey = DateTime.now().millisecondsSinceEpoch.toString();
      shifts[newKey] = '';
      // 为新项创建控制器
      keyControllers[newKey] = TextEditingController(text: newKey);
      valueControllers[newKey] = TextEditingController(text: '');
    });
  }

  void deleteShift(String key) {
    setState(() {
      shifts.remove(key);
      // 删除对应控制器并销毁
      keyControllers[key]?.dispose();
      valueControllers[key]?.dispose();
      keyControllers.remove(key);
      valueControllers.remove(key);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: ListView.builder(
            itemCount: shifts.keys.length,
            itemBuilder: (context, index) {
              String key = shifts.keys.elementAt(index);

              return ListTile(
                title: Row(
                  children: [
                    Expanded(
                      child: TextFormField(
                        controller: keyControllers[key],
                        onChanged: (newValue) {
                          // 同步更新Map中的键
                          setState(() {
                            final value = shifts[key]!;
                            shifts.remove(key);
                            shifts[newValue] = value;
                            // 更新控制器的键映射
                            keyControllers[newValue] = keyControllers[key]!;
                            keyControllers.remove(key);
                            valueControllers[newValue] = valueControllers[key]!;
                            valueControllers.remove(key);
                          });
                        },
                      ),
                    ),
                    SizedBox(width: 10),
                    Expanded(
                      child: TextFormField(
                        controller: valueControllers[key],
                        onChanged: (newValue) {
                          // 同步更新Map中的值
                          setState(() {
                            shifts[key] = newValue;
                          });
                        },
                      ),
                    ),
                  ],
                ),
                trailing: IconButton(
                  icon: Icon(Icons.delete),
                  onPressed: () {
                    deleteShift(key);
                  },
                ),
              );
            },
          ),
        ),
        ElevatedButton(
          onPressed: addShift,
          child: Text('Add Shift'),
        ),
      ],
    );
  }
}

额外说明

  • 新增了onChanged回调,实现输入框内容与Map数据的实时同步。
  • 在initState中初始化现有项的控制器,dispose中销毁所有控制器,避免内存泄漏。
  • 使用时间戳作为新项的键,确保唯一性,也可以根据需求替换为自增ID等其他唯一标识方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 03:16:03