Flutter开发Todo应用时新建待办标题描述重复该如何解决
问题根因
你代码的核心问题有3个:
- 所有列表项的输入框共用了全局的2个
TextEditingController,控制器内容变化时所有绑定的输入框会同步更新,自然所有待办内容完全一致 todos数组仅存储空字符串,没有为每个待办单独设计存储标题、描述的数据结构Dismissible的Key值重复,后续做删除操作时会出现逻辑异常
修复方案
首先先定义Todo实体类,用来存储单个待办的独立数据,可直接写在HomePage类外面:
class Todo { final String id; String title; String detail; Todo({ required this.id, this.title = '', this.detail = '', }); }
修改State内的代码,完整修改后如下:
class _HomePageState extends State<HomePage> { // 替换原有的todos数组,类型改为Todo列表 List<Todo> todos = []; @override void initState() { super.initState(); // 初始新增一个空Todo,用毫秒时间戳做唯一id todos.add(Todo(id: DateTime.now().millisecondsSinceEpoch.toString())); } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 24.0, ), color: const Color(0xfff6f6f6f6), child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: const EdgeInsets.only(bottom: 32.0, top: 32.0), child: const Text( "Reminders", style: TextStyle( fontSize: 25.0, fontWeight: FontWeight.bold ), ), ), Expanded( child: ListView.builder( itemCount: todos.length, physics: const BouncingScrollPhysics(), itemBuilder: (BuildContext context, int index) { final currentTodo = todos[index]; return Dismissible( // 用Todo的唯一id做Key,避免重复 key: Key(currentTodo.id), // 补充删除逻辑 onDismissed: (direction) { setState(() { todos.removeAt(index); }); }, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric( vertical: 20.0, horizontal: 24.0 ), margin: const EdgeInsets.only( bottom: 20.0, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( cursorColor: Colors.black, autofocus: index == todos.length -1, // 只有新增的待办自动聚焦 // 移除全局controller,用onChanged实时更新当前Todo的标题 onChanged: (value) { setState(() { currentTodo.title = value; }); }, // 绑定当前Todo的标题为初始值 controller: TextEditingController(text: currentTodo.title), style: const TextStyle( fontSize: 22.0, fontWeight: FontWeight.bold ), decoration: const InputDecoration( hintText: "Enter a title", border: InputBorder.none, ), ), const Divider( color: Colors.black, ), Padding( padding: const EdgeInsets.only(top: 0.0), child: TextField( // 移除全局controller,用onChanged实时更新当前Todo的描述 onChanged: (value) { setState(() { currentTodo.detail = value; }); }, // 绑定当前Todo的描述为初始值 controller: TextEditingController(text: currentTodo.detail), style: TextStyle( fontSize: 20.0, color: Colors.grey[900], ), cursorColor: Colors.black, maxLines: null, decoration: const InputDecoration( hintText: "Enter the description", label: Text("description"), border: InputBorder.none ), ), ), ], ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20.0) ), ), ); }, ), ) ], ), Positioned( bottom: 24.0, right: 0.0, child: GestureDetector( onTap: () { setState(() { // 新增时创建新的Todo实例,和已有待办完全独立 todos.add(Todo(id: DateTime.now().millisecondsSinceEpoch.toString())); }); }, child: Container( width: 60.0, height: 60.0, decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(20.0), ), child: const Icon(Icons.add, color: Colors.white, size: 35.0), ), ), ) ], ), ), ), ); } }
核心修改点说明
- 每个待办都有独立的Todo实例存储标题和描述,数据完全隔离不会互相干扰
- 移除了全局输入控制器,每个输入框单独绑定对应待办的数据,修改只会作用到当前待办
- 优化了自动聚焦逻辑,只有新增的最后一条待办会自动触发聚焦,符合用户操作习惯
- 补充了删除待办的逻辑,滑动删除可以正常生效
内容的提问来源于stack exchange,提问作者Rohith Nambiar
相关产品推荐
相关产品推荐

