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();
}