I was building a todo list app which uses a ListView.builder
to render multiple task widgets which have three properties, title, checked, and starred. It works just fine until I star/check an existing task and add a new task, in which case the state of the previous task seems to jump to the newly added task. What could be causing the problem? Could this have something to do with keys?
class Main extends StatefulWidget {
@override
State<Main> createState() => _MyAppState();
}
class _MyAppState extends State<Main> {
var todoList = [];
var stars = 0;
void addStar() {
setState(() {
stars++;
});
}
void removeStar() {
if (stars > 0) {
setState(() {
stars--;
});
}
}
void addTodo(title) {
setState(() {
todoList.add(title);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
floatingActionButton: Builder(builder: (context) {
return FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return NewTodo(addTodo: addTodo);
});
},
);
}),
body: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'My Day',
style:
TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
Container(
child: Row(
children: [
const Text(
'IMPORTANT: ',
style: TextStyle(
fontSize: 16.0,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 58, 58, 58),
),
),
Text(
'$stars',
style: const TextStyle(
fontSize: 24.0,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 253, 147, 8)),
),
const SizedBox(width: 20.0),
IconButton(
icon: Icon(Icons.more_vert, size: 24),
onPressed: () => {print('more ...')},
),
],
),
)
],
),
),
Expanded(
// height: 300,
child: todoList.length == 0
? Text('No Tasks Yet 💪',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.w600,
color: Colors.grey))
: ListView.builder(
itemCount: todoList.length,
itemBuilder: (context, index) {
return Todo(
title: todoList[(todoList.length - 1) - index],
addStar: addStar,
removeStar: removeStar);
}))
],
),
),
),
);
}
}
the app currently only has three files, below is a link to the gist.
https://gist.github.com/FENETMULER/eb4a898b82f9aa4c6a871a1fa9833c84
The solution is to use Key
s with a ListView
and not a ListView.builder
as it doesn't seem to work with ListView.builder
, the problem actually comes from reversing the list, since when the list is reversed every time a new task is added the objects in the Widget Tree
won't be in line with their corresponding Object in the Element Tree
.
ListView(children: todoList.reversed.map((title) => Todo(title: title, key: ValueKey(title))).toList())