[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]