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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 21:45:04