First, I want to correct that sentence:
Why doesn't Flutter create a key for elements by default
Flutter actually does create a Key
by default for all widgets, from the official documentation of key:
If the runtimeType and key properties of the two widgets are operator==, respectively, then the new widget replaces the old widget by updating the underlying element (i.e., by calling Element.update with the new widget).
so by default, the Key
of a widget is related directly to its runtimeType
,
and what causes the issue in updating the state, where two widgets or more with the same Key
(which we said is the same, and based on the widget runtimeType
) get conflicted which causes that behavior.
You might be asking now, why Flutter even uses the runtimeType
, why it doesn't use something like the hashcode
or uuid
or some Random()
for example as a Key
...
and the answer is, that when we add our widget to the widget tree and try to update its state, Flutter doesn't take off the whole widget from the tree and insert it again, it looks up for something that has that key, which is the widget using it, then compare the State
objects, then replace one with the other.
The purpose of that algorithm which works like this is to get the most performant and lightweight state update, which makes Flutter rebuilds widgets very fast.
Read more from this, and this.
in the cases where this causes the weird behavior of an old widget to get updated with a new widget, Flutter offers multiple Key
solutions so that the framework knows that every widget is separated from the other one even if they have the same runtimeType
, which is UniqueKey()
, GlobalKey()
, valueKey()
, PageStorageKey()
...
and every single one is different than the other and has its best use case.
as for the example of the old widget getting updated by a new one on the same screen, can be fixed by only assigning any key from those, but the ValueKey()
which returns a Key
based on a value of any object like ValueKey(1)
as an example, or UniqueKey()
where UniqueKey() == UniqueKey()
is always false
are just enough.
In another case, where you will use the same widget in two different screens/routes in your app, and you want them to have the same state, a GlobalKey()
will fit in that case.
In another case where you have multiple scrollable widgets like ListView.builder()
or a SingleChildScrollView()
..., and you want to scroll to some position then navigate or change tabs (as an example)..., and the scroll position gets remembered when you get back to it, then the PageStorageKey()
will fit that case.
You can read more about keys from the :
Official documentation
And this