Flutter待办应用添加同名待办项崩溃问题求助
修复Flutter待办应用添加同名项崩溃的问题
你的猜测完全正确——崩溃的核心原因是列表子项的Key值重复。Flutter的ReorderableListView要求每个子Widget的Key必须全局唯一,你当前用ValueKey(storedText[index])作为Key,当添加同名待办项时,多个子项会共享同一个Key,导致Flutter无法正确追踪Widget状态,最终引发崩溃。
解决方案:用唯一标识的待办数据模型替代分离的列表
最规范的修复方式是创建一个包含唯一ID的Todo数据类,将待办文本和完成状态封装在一起,同时用唯一ID作为Key。这样既解决了Key重复问题,也让数据结构更清晰易维护。
步骤1:创建Todo数据类
class Todo { final String id; final String text; bool isCompleted; Todo({ required this.id, required this.text, this.isCompleted = false, }); }
这里用DateTime.now().microsecondsSinceEpoch.toString()生成唯一ID,无需额外依赖包,足以满足基础应用需求。
步骤2:修改状态管理逻辑
将原来的两个分离列表storedText和checkedValue替换为单个Todo列表:
List<Todo> todos = [];
步骤3:更新添加待办的逻辑
onSubmitted: (value) { setState(() { todos.insert(0, Todo( id: DateTime.now().microsecondsSinceEpoch.toString(), text: value, )); _textyController.clear(); }); },
步骤4:重构ReorderableListView
更新itemBuilder和onReorder逻辑,基于Todo对象操作:
ReorderableListView.builder( reverse: true, itemCount: todos.length, onReorder: (oldIndex, newIndex) { setState(() { if (oldIndex < newIndex) { newIndex -= 1; } final Todo todo = todos.removeAt(oldIndex); todos.insert(newIndex, todo); }); }, itemBuilder: (context, index) { final todo = todos[index]; return Padding( key: ValueKey(todo.id), // 使用唯一ID作为Key padding: const EdgeInsets.symmetric(vertical: 8.0), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.yellow), child: Padding( padding: const EdgeInsets.all(8.0), child: CheckboxListTile( activeColor: Colors.black, controlAffinity: ListTileControlAffinity.leading, title: Text(todo.text), value: todo.isCompleted, onChanged: (newCheckedValue) { setState(() { todo.isCompleted = newCheckedValue ?? false; }); }), ), ), ); }, ),
完整修改后的代码
import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( home: const TodoApp(), theme: ThemeData(primarySwatch: Colors.yellow), )); } class Todo { final String id; final String text; bool isCompleted; Todo({ required this.id, required this.text, this.isCompleted = false, }); } class TodoApp extends StatefulWidget { const TodoApp({super.key}); @override State<TodoApp> createState() => _TodoAppState(); } class _TodoAppState extends State<TodoApp> { final _textyController = TextEditingController(); List<Todo> todos = []; @override Widget build(context) { return Scaffold( backgroundColor: Colors.yellow[100], appBar: AppBar( title: const Text("Todo"), centerTitle: true, elevation: 0, ), body: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 15.0), child: Column( children: [ Expanded( child: ReorderableListView.builder( reverse: true, itemCount: todos.length, onReorder: (oldIndex, newIndex) { setState(() { if (oldIndex < newIndex) { newIndex -= 1; } final Todo todo = todos.removeAt(oldIndex); todos.insert(newIndex, todo); }); }, itemBuilder: (context, index) { final todo = todos[index]; return Padding( key: ValueKey(todo.id), padding: const EdgeInsets.symmetric(vertical: 8.0), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.yellow), child: Padding( padding: const EdgeInsets.all(8.0), child: CheckboxListTile( activeColor: Colors.black, controlAffinity: ListTileControlAffinity.leading, title: Text(todo.text), value: todo.isCompleted, onChanged: (newCheckedValue) { setState(() { todo.isCompleted = newCheckedValue ?? false; }); }), ), ), ); }, ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 20.0), child: TextField( controller: _textyController, onSubmitted: (value) { setState(() { todos.insert(0, Todo( id: DateTime.now().microsecondsSinceEpoch.toString(), text: value, )); _textyController.clear(); }); }, decoration: const InputDecoration( labelText: '+ Add a task', labelStyle: TextStyle( color: Colors.grey, ), border: OutlineInputBorder(), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusColor: Colors.blue, ), cursorColor: Colors.grey[400], ), ), ], )), ); } }
额外说明
如果你暂时不想重构数据结构,也可以用ValueKey('${storedText[index]}_$index')这种方式拼接索引生成唯一Key,但这种方式在列表重排后可能引发状态异常,不如数据模型的方式可靠,因此更推荐前者。
内容的提问来源于stack exchange,提问作者Hans Ramzan
相关产品推荐
相关产品推荐

