RangeError(RangeError(索引):无效值:有效值范围为空:0)从警报对话框的列表中删除项目时出错
我对 flutter 还很陌生,正在学习一门课程,但遇到了这个错误:
RangeError (RangeError (index): Invalid value: Valid value range is empty: 0)
背景
我正在编写的用于学习 flutter 的简单应用程序是一个任务管理应用程序,我们有一个任务列表,您可以单击按钮将新任务添加到列表中。
查看任务的主列表时,您可以单击“查看”,然后弹出一个警报对话框,在警报对话框中显示任务“标题”和任务“描述”。
在警报对话框中,我有一个“保存”和“删除”按钮。保存工作正常,我在删除按钮方面遇到问题。
代码
class Rivers extends StatelessWidget {
const Rivers({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Wave a Task'),
),
body: Column(
children: const [
CircleAvatar(
radius: 40,
backgroundImage: AssetImage('images/wave.png'),
),
RiverBody(),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => TaskPage(),
),
);
},
child: const Icon(Icons.add),
),
);
}
}
class RiverBody extends StatelessWidget {
const RiverBody({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView(
padding: const EdgeInsets.only(top: 10.0),
children: List.generate(
Provider.of<Tasks>(context).tasks.length,
(index) => ListTileCustom(
index: index,
),
),
),
);
}
}
class ListTileCustom extends StatelessWidget {
const ListTileCustom({Key? key, required this.index}) : super(key: key);
final int index;
@override
Widget build(BuildContext context) {
Task currentTask = Provider.of<Tasks>(context).tasks[index];
return CheckboxListTile(
subtitle: Align(
alignment: Alignment.centerLeft,
child: TextButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
String title = Provider.of<Tasks>(context).tasks[index].title;
String description =
Provider.of<Tasks>(context).tasks[index].description;
TextEditingController titleController =
TextEditingController(text: title);
TextEditingController descriptionController =
TextEditingController(text: description);
return AlertDialog(
title: TextField(
controller: titleController,
),
content: TextField(
controller: descriptionController,
),
actions: [
TextButton(
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: Colors.red,
),
onPressed: () {
Provider.of<Tasks>(context, listen: false)
.deleteTask(index: index);
Navigator.of(context).pop();
},
child: const Text('Delete'),
),
TextButton(
onPressed: () {
Provider.of<Tasks>(context, listen: false).save(
index: index,
newTitle: titleController.text,
newDescription: descriptionController.text);
Navigator.of(context).pop();
},
child: const Text('Save'),
),
],
);
});
},
child: const Text('View'),
),
),
onChanged: (newbool) {
Provider.of<Tasks>(context, listen: false)
.changeDone(index: index, isDone: newbool!);
},
value: currentTask.done,
title: Text(currentTask.title),
);
}
}
当我单击删除按钮时,错误发生在第 74 行:
String title = Provider.of<Tasks>(context).tasks[index].title;
我认为发生的情况是,当我单击“删除”按钮并且它在按下的函数上运行时:
onPressed: () {
Provider.of<Tasks>(context, listen: false)
.deleteTask(index: index);
Navigator.of(context).pop();
},
child: const Text('Delete'),
),
我认为正在破坏的代码因为警报对话框中的“标题”来自我刚刚删除的索引...所以在运行弹出窗口之前,警报对话框有一瞬间没有任何内容可显示...
我尝试移动 Navigator.of (context).pop();
在前面删除电话,但这也不起作用。如果我继续执行代码,任务确实会被删除,所以它可以工作,但不确定如何修复弹出的错误。
删除任务函数是:
void deleteTask({required int index}) {
tasks.removeAt(index);
notifyListeners();
}
I am pretty new to flutter and am following a course and ran into this error:
RangeError (RangeError (index): Invalid value: Valid value range is empty: 0)
Background
The simple app I am writing to learn flutter is a task management app where we have a list of tasks, and you can click a button to add new tasks to the list.
When viewing the main list of tasks you can click "View" and an Alert Dialog pops up showing the Task "title" and task "description" in an Alert Dialog.
In the Alert Dialog I have a "save" and "delete" button. The save works fine, I am having issues with the delete button.
Code
class Rivers extends StatelessWidget {
const Rivers({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Wave a Task'),
),
body: Column(
children: const [
CircleAvatar(
radius: 40,
backgroundImage: AssetImage('images/wave.png'),
),
RiverBody(),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => TaskPage(),
),
);
},
child: const Icon(Icons.add),
),
);
}
}
class RiverBody extends StatelessWidget {
const RiverBody({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView(
padding: const EdgeInsets.only(top: 10.0),
children: List.generate(
Provider.of<Tasks>(context).tasks.length,
(index) => ListTileCustom(
index: index,
),
),
),
);
}
}
class ListTileCustom extends StatelessWidget {
const ListTileCustom({Key? key, required this.index}) : super(key: key);
final int index;
@override
Widget build(BuildContext context) {
Task currentTask = Provider.of<Tasks>(context).tasks[index];
return CheckboxListTile(
subtitle: Align(
alignment: Alignment.centerLeft,
child: TextButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
String title = Provider.of<Tasks>(context).tasks[index].title;
String description =
Provider.of<Tasks>(context).tasks[index].description;
TextEditingController titleController =
TextEditingController(text: title);
TextEditingController descriptionController =
TextEditingController(text: description);
return AlertDialog(
title: TextField(
controller: titleController,
),
content: TextField(
controller: descriptionController,
),
actions: [
TextButton(
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: Colors.red,
),
onPressed: () {
Provider.of<Tasks>(context, listen: false)
.deleteTask(index: index);
Navigator.of(context).pop();
},
child: const Text('Delete'),
),
TextButton(
onPressed: () {
Provider.of<Tasks>(context, listen: false).save(
index: index,
newTitle: titleController.text,
newDescription: descriptionController.text);
Navigator.of(context).pop();
},
child: const Text('Save'),
),
],
);
});
},
child: const Text('View'),
),
),
onChanged: (newbool) {
Provider.of<Tasks>(context, listen: false)
.changeDone(index: index, isDone: newbool!);
},
value: currentTask.done,
title: Text(currentTask.title),
);
}
}
When I click the delete button the error happens at line 74:
String title = Provider.of<Tasks>(context).tasks[index].title;
What I think is happening is that when I click the "delete" button and it runs its on pressed function:
onPressed: () {
Provider.of<Tasks>(context, listen: false)
.deleteTask(index: index);
Navigator.of(context).pop();
},
child: const Text('Delete'),
),
The code I think is breaking because the "title" in the Alert Dialog comes from the index I just deleted... so for a split second the alert dialog has nothing to show right before it runs the pop...
I tried moving the Navigator.of(context).pop();
in front of the delete call but that didn't work either. If I continue the code the task does get deleted, so it kind of works but not sure how to fix the error popping up.
The deleteTask function is:
void deleteTask({required int index}) {
tasks.removeAt(index);
notifyListeners();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果这是你的情况,对话框正在使用你正在删除的任务,你可以在两者之间添加一个微小的延迟 - 你甚至可以弹出另一个小对话框,上面写着“删除任务”,就像一个很好的用户体验。
我会做类似的事情:
作为一个很好的东西(以下内容是可选的):
在显示旋转的圆形进度指示器和 后,我会立即显示一个小对话框文本小部件只是说“删除任务”或类似的内容。
我将按照上面的方式重构它,但现在我传递一个回调,如下所示:
在 onPressed 中,我将执行相同的操作,但在弹出主警报后,我将启动“删除任务”,在延迟完成并且回调执行它时,再次弹出导航器以将其关闭:
这是 Gist 详细介绍了该方法(在 DartPad.dev 上运行并查看)。您应该看到以下输出:
If that's your case, that the dialog is using the task you are deleting, you could add a tiny delay in between - you could even pop another small dialog that's saying "deleting task" just as a nice user experience.
I'd do something like:
As a nice-to-have (the below stuff is optional):
I'd show a small dialog right after just showing a spinning circular progress indicator and a Text widget just saying "deleting task" or something like that.
I'd refactor it as above, but now I pass a callback, as such:
And in the onPressed, i'd perform the same action, but right after popping the main alert, I'd launch the "deleting task" one, and upon the delay being done and the callback execute it, pop the Navigator once again to dismiss it:
Here's the Gist that details pretty much the approach (run it on DartPad.dev and see). You should see the following output: