Flutter - UI is not updated correctly when removing an element
This is not a solution but an explanation, refer to Marcin Szałek's comment for the solution.
The reason that the ListView
does not rerender as expected is that the TextFormField
is a stateful widget. When you rebuild the widgets, you need to take note that there are 3 trees under the hood, which are the Widget, Element and RenderObject tree. The RenderObject is the visual representation, and is directly based on the Element. Whereas the Element is directly based on the Widget, and the Widget is just the configuration, i.e. what you type in code.
When first instantiating a widget, a corresponding element is created. However, when you call setState()
to change the _items list, the widget is rebuild, but the element is not destroyed and recreated. Rather, in order to maximise efficiency, Flutter is made such that it first compares any differences between the initial ListTile
widgets and the rebuild ListTile
widgets. In this case, initially there are 4 tiles, and afterwards there are 3 tiles. Instead of recreating new tiles, the elements of the second and third tiles are updated to have the attributes of the rebuild ListTile
widgets (and no changes occur for the first tile because the widget/configuration before and after is the same). The 4th element is popped from the Element tree. This video by the official Flutter team does a great job at explaining the three trees.
Using Dart's Devtools, you can find no change in the key/id of the third element. When you inspect the third ListTile
, before and after the key is #3dded.
Initial
After deletion
To remove the elements from the tree and then put them back in, attach a key to the ListView
as commented by Marcin Szałek. For stateful widgets, elements will be updated when the corresponding new widget is of the same widget type and has the same key. Without explicitly specifying the key, the element will only check whether it is of the same type as the corresponding widget, and as all the widgets are of runtime type ListTile
, then there would not be any update of the elements, thus you see item 0 and item 1 instead of item 0 and item 2. I recommend that you read more about keys here.
Using Dart's Devtools, you can find the change in the key of the third ListTile
. Initially, when it was item 1, the key was #5addd. Then, when item 1 is deleted, the key changes to #26044.
Initial
After deletion
The reason for removing not updating the deleted element is the second widget is stateful widget it needs force rendering in some case
use this code to get reload the view by overriding the didUpdateWidget method
@override
void didUpdateWidget(LoadImageFromPathAsync oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget != widget) {
setState(() {});
}
}
What you can do is pass to the ListView
the Key
which will contain the length of the elements in the list.
ListView(
key: Key(myList.length.toString()),
children: myList.map(...),
),
Why it works?
It causes the whole ListView
to rebuild itself from scratch but only when the length on your list changes. What's important is that it doesn't happen in other cases (like typing inside a TextField) because such action would cause you to lose focus on every letter type.
Hope it helps ;)