[MUSIC PLAYING]
EMILY: Hey, I'm Emily from the Flutter team.
In this video, we're going to talk about using
the key parameter on widgets.
If you're wondering what the heck a widget is,
I recommend going back to the first video
in the series, where we introduced the building
blocks of Flutter.
Keys preserve state when widgets move around
in your widget tree.
They can be used to preserve the user's scroll location,
or keeping state when modifying a collection.
In this video, we'll cover three things--
when you need keys, where to put them in your widget tree,
and which key is right for you.
[BELL DINGING]
So when do you need to use keys?
Most of the time, you don't.
But if you find yourself adding, removing, or reordering
collection of widgets of the same type that hold some state,
it's key time.
No, key time.
An example of this is a to-do list app.
When using the app, you want to reorder the items based
on your to-do list based on priority,
and then remove them when you're done.
Another example is the Hacker News app in the Flutter Boring
Show.
Adding keys prevented the story widgets
from incorrectly picking up old state when
the list view was rebuilt.
To illustrate why you need keys in this situation,
I wrote an extremely simple app with two randomly colored
widgets that swap places when you tap a button.
Here's the code for the app.
It consists of a stateful widget,
called PositionedTiles, that constructs two stateless tiles,
and places them in a row.
When I tap the Floating Action button down at the bottom,
it swaps their position in the list, as expected.
But what happens if we make those colorful tiles
stateful instead of stateless, and store
the color in the state?
Now, when I hit the button, it looks like nothing is changing.
But if I add a key to each tile widget,
the widgets swap places like you expect.
Remember, though, if the entire widget
subtree in your collection is stateless, keys aren't needed.
Technically, this is all you need
to know to use keys in your apps for most simple situations.
Now that everyone's left, we can go
into how keys work under the covers
when Flutter renders these widgets.
In the stateless widget version, the row widget
has a set of ordered slots for its children.
As you've seen in the earlier episodes of the series,
for every widget, Flutter builds a corresponding element.
The element tree is extremely simple,
only holding information about the type
of each widget and a reference to children elements.
You can think of the element tree
like a skeleton of your Flutter app.
It shows the structure of your app,
but all the additional information
can be looked up via reference to the original widget.
When we swap the order of the tile widgets in the row,
Flutter walks the element tree to see
if the skeletal structure is the same.
It starts with the row element and then moves to its children.
The element tree checks that the new widget is the same type
and key as the old one.
And if so, it updates its reference to the new widget.
In this case, the widgets don't have keys,
so Flutter just checks the type.
It does the same for the second child.
Now, let's run through the same scenario again,
only this time with stateful tile widgets.
You can see I've got the same widgets and elements as before,
but now there are a pair of state objects with them.
And the color information is being stored there, not
in the widgets themselves.
So this time, when I swap the order of the two widgets,
Flutter walks the element tree, checks
the type of the row widget, and updates the reference.
Then, tile element checks that the corresponding widget is
the same type-- a tile widget--
and it is.
And it does the same for the second child.
Flutter uses the element tree and its corresponding state
to determine what to actually display on your device.
So from our perspective, it looks like your widgets
didn't properly swap.
In the second version, with the stateful tiles,
I added key properties to the widgets.
Now if we swap the widgets, the row widgets match like before,
but the key of the tile element doesn't
match the key of the corresponding tile widget.
So Flutter deactivates those elements,
moving the references to the tile elements in the element
tree, starting with the first one that doesn't match.
Then Flutter looks through the non-matched children
for an element with the corresponding key.
It finds a match, and updates its reference
to the corresponding widget.
Flutter then does the same thing for the second child.
Now, Flutter will display what we expect, with the widgets
swapping places and updating their color.
So, in summary, keys are useful if you're
modifying the order or number of stateful widgets in a collection.
For the sake of illustration, I stored the color as state
in this example.
Often, though, state is much more subtle.
Playing animations, displaying data that the user has entered,
and scroll location all involve state.
So you have a scenario where you need a key for your app.
Where do you put it?
The answer is to specify a key at the top of the widget
subtree that you need to preserve.
Since we've been talking about state so much,
you might think that it is the first stateful widget.
But (SPEAKING WITH STERN MONOTONE) you'd be wrong.
To show you why, I wrapped my colorful tile widgets
with padding widgets, but I left the keys on the tiles.
Now when I click the button, the tiles
change to completely different random colors.
What's going on?
Here's what the widget tree and the element tree
look like with the padding widgets added.
When we swapped the positions of the children,
Flutter's element-to-widget matching algorithm
looks at one level of the tree at a time.
I grayed out the children's children in the diagram
so we can focus on one level at a time.
At that first level of children, everything
matches up correctly.
At the second level, Flutter notices
that the key of the tile element doesn't
match the key of the widget.
So it deactivates that tile element,
dropping those connections.
The keys we're using in this example are local keys.
That means that when matching up widgets to elements,
Flutter only looks for a key that
matches within a particular level in the tree.
Since it can't find a tile element
at that level with that key value, it creates a new one,
and initializes a new state.
In this case, making the widget orange.
The same problem happens for the other tile.
If we add keys at the level of the padding widgets,
Flutter notices the problem, and updates the connections
correctly, just like it did in our previous example.
Order is restored in the universe.
So now you know when to use keys and where to put them.
But if you looked at the Flutter documentation,
you'll see there are several different kinds of keys.
So which one should you use?
In the scenario where you're modifying
a collection of widgets, like the swapping colored tiles
example, you only need to distinguish between the keys
of other children.
Take a look at the information you're
storing in those child widgets to help
understand what widget to use.
In a to-do list app, we might expect the text of a to-do item
to be constant and unique.
If that's the case, it's probably
a good candidate for a value key,
where the text is the value.
Now suppose that instead, each child widget
stores a more complex combination of data.
Perhaps you have an address book app that listed information
about each user.
Any of the individual fields, like a first name
or a birthday, might be the same as another entry,
but the combination is unique.
In this scenario, an object key is probably most appropriate.
If you have multiple widgets in your collection
with the same value, or if you want to really ensure
that each widget is distinct from all others,
you can use the UniqueKey.
I used the UniqueKey in the example app
because we didn't have any other constant data that we're
storing on our tiles, and we don't
know what the color will be until we construct the widget.
But one thing you don't want to use
is a random number in your key.
Every time a widget gets built, a new random number
will be generated, and you'll lose
the consistency between frames.
Then you might not as well have used keys in the first place.
Page storage keys are specialized keys
that store a user's scroll location so the app can
preserve it for later.
Global keys have two uses.
They allow widgets to change parents anywhere in your app
without losing state, or they can
be used to access information about another widget
in a completely different part of the widget tree.
An example of the first scenario might
be if you want to show the same widget on two
different screens, but holding all the same states.
You'd want to use a global key.
In the second scenario, maybe you
want to validate a password, but you
don't want to share that state information
with other widgets in the tree.
Often, though, global keys are a little like global variables.
There is usually a better way to look up
that state using inherited widgets,
or something like redux or the block pattern.
So, in summary, use keys when you want to preserve state
across widget trees.
This most commonly occurs when you're
modifying a collection of widgets of the same type,
like in a list.
Put the key at the top of the widget tree you want preserved,
and choose the key type that you're
using based on the type of data you're storing in that widget.
For more documentation, check out flutter.io.
Happy creating.
[MUSIC PLAYING]