Keys in Flutter - Dive Deep
Muhammad Adnan
Flutter Software Engineer | Helping founders successfully land their Android & iOS apps | Empowering people on YouTube | Firebase & Node.js Expert
Did you ever use keys to unlock a door ? I know the answer, is,?“YES”. Today we will use keys to unlock Flutter widgets. You may have definitely seen this poor?Key?object, every time when you create a stateless or stateful widget.
But, what that?“THING”?key actually means? is that any secret key to unlock a door, sorry I mean a?“widget”? Exactly. Key in flutter is used to uniquely identify widgets in the widget tree and helps flutter keep track of them when they are added, removed, or rearranged in a user interface.
Watch Video or continue reading
When do we use keys ?
Well, most of the time we don’t, but they can be used to preserve state when widgets move around in the widget tree.
In this example, of a simple todo app, reordering the todo based on the priority, and remove when it’s done,?the keys here are very important. Here, when items are reordered, Flutter needs to determine which TodoItem widget need to be updated with new position. The unique keys of each TodoItem widgets enables Flutter to recognize that each TodoItem has its own identity. Since each TodoItem widget has a unique key, Flutter can efficiently update the positions of the widgets in the widget tree, maintaining individual states during reordering. This prevents state loss and ensures that the TodoItem remain their correct state even after the reordering.
And that’s it, that’s all what you need to know when to use keys.
But “we’re confused developers, we cannot get sigh! unitl we reach to the deep of a anything we’re exploring and understand what’s actually happening under the hood”
Be confused, it’s where you begin to learn new things — S.C Lourie
Keys under the hood
To understand keys under the hood look at this very simple app below, where we have 2 count values, and a “swap” button. “And you definitely know what will happens when is clicked ??”.
And here's the code
In the code, we have a stateful widget?NumbersApp?which constructs 2 stateless widgets of?StatefulNumber, in the?Column?children. And when the swap Elevated button is tapped, it swap between two numbers. and it works fine, BUT the magic really starts to happen when we change the?StatefulNumber?to the?StatefulNumber.
@override
void initState() {
children = [
StatefulNumber(),
StatefulNumber(),
];
super.initState();
}
This time the number will be stored in the state of the stateful StatefulNumber.
We’re firing off the swap button like a gun trigger but no swaps seems to be happening.
But if we add?UniqueKey()?to each?StatefulNumber?widgets, everything starts working as it was before.
@override
void initState() {
children = [
StatefulNumber(key: UniqueKey()),
StatefulNumber(key: UniqueKey()),
];
super.initState();
}
Note: in case of dealing with stateless widgets we don’t need any kind of keys, but instead we directly deal with type and perform necessary updates for swapping.
However, there are some cases where you might want to use keys with stateless widgets. For example, if you are using a stateless widget in a list, you might want to use a key to track the position of the widget in the list. This can be useful if you need to update the widget’s state based on its position in the list.
In general, you should only use keys with stateless widgets if you need to track the widget’s identity or position. If you don’t need to do either of these things, then you can safely omit the key.
Now I’m going to reveal a cutest face of flutter under the hood, which could form goose bumps on newbie flutter devs. But hey! you don’t need to worry because?“I’m here”?to explain everything, Yes, almost?“EVERYTHING”.
Happy Trees Day.
Oh, sorry that’s too much! I was about to show you this structure you can see below:
If this sound abstract to you, then you might have missed something. Such as learning?“how flutter render widgets”?? But there’s no need to worry, you can either watch?my video ?on?YouTube?or read?my blog post? on?Medium.
In case of stateless widgets
So, now I assume you know what is tree structure and how flutter use it to render widgets, Let’s start from where we had Stateless widgets for swapping.?Flutter?builds the corresponding elements for each widget in the widget tree and this tree is called an element tree.
Widgets?are itself immutable, and are just configurations that how the UI should look like, and the corresponding?Elements?are responsible for keeping the information about the specific type of widget and reference to children elements same as we have in the widget tree.
And to lookup additional information, the?Elements?in the?Element?tree?references to the original widgets.
When the widgets swaps,?Flutter?framework?checks through?Element?tree, It starts checking in our case the?Column?Element?and then move to it’s children, here the element tree will call this method.
Checking for the type and the keys, to accordingly update?renderObjects ?to display a new updated result on the UI, in this case we don’t have any keys so it will ignore keys, and the widget that is just swapped is of same type, then it will check the same for the second child, and nothing was destroyed and recreated but just little updates happens when the swap button was clicked.
In case of statelful widgets
Now, everything remain the same but in case of?stateful widgets, we will introduce a pair of?state?with each element. And this time the number is stored in the state, instead of the widget itself. So when the widgets swaps, again?canUpdate?method was called. And the process starts from the?Column?Element?to it’s children and checks for the types and keys and yet we don’t have keys again so it doesn’t matter in this case also, so it checks first child, type is same?“Yes”?for the second child?“Absoloutely”.
Note: Flutter framework uses the Elemet tree and it’s corresponding state to determine what to display on the UI, so in our case the state remain unchanged and we didn’t get any result on swap on the UI, which means our widgets didn’t swapped properly.
In case of statelful widgets with keys
This time, if we add?UniqueKey()?to each?stateful widgets?(StatefulNumber), and swaps the widgets, again the?canUpdate()?method knows its job. The process starts, for the parent widget everything remain the same, and for the children looking for the runtimeTypes yes both are the same without any doubt… BUT wait, the keys aren’t same.
领英推荐
So, in that case what flutter does is to deactivate the elements references, and again starts checking for the correct match, for the?Column?again everything remain the same, and the first child widget doesn’t match because this is the one because of which the references were deactivated on swap, now the?flutter?framework?will looks for other non-matched children for the element with a corresponding key, so yes it gots the key matched and updates it’s reference to the corresponding key widget, and the same for the other child.
So, becuase of keys, when swap happens we got what we expected to see on the screen.
where to specify keys?
Keep in mind, always put keys at the outermost stateful widget that you want to preserve state for. This is because keys are used by?Flutter?to identify individual widgets in a widget tree, and if you only put a key on a child widget,?Flutter?will not be able to track the state of the parent widget.
And if you think we’re talking about the state of our first stateful widget that we have for swap, So, its, not like that. Because, when we wrap?StatefulNumber?widgets having?UniqueKey?in their constructor with a?Container?widget.
@override
void initState() {
children = [
Container(child: StatefulNumber(key: UniqueKey())),
Container(child: StatefulNumber(key: UniqueKey())),
];
super.initState();
}
now if we swap the widgets, now it’s generating random numbers on each swap, what’s wrong with this?
Well, Let’s look at the tree structure.
So, initially our widget tree looks something like this with the?Container?added on top of the?StatefulNumber?widgets within the keys in their constructor.
Now when we swap two?StatefulNumber?widgets, the?Flutter?element-to-widget matching algorithm,?looks at one level of the tree at the time.
in that first level of the children everything mateches correctly, and in the second level , the widgets key didn’t match, and the element connection is deactivated.
Because when you use a local key in a child widget, it will not be used to match the widget to an element in the parent widget. This is because Flutter only looks for keys that match in the same level of the tree.
And, flutter framework didn’t find the?StatefulNumber?element at that level with that key value.
As a result, when you rebuild, the?Containerwidget will be recreated, and the?UniqueKey?of the?StatefulNumber?widget will be different. This is why the numbers start generating randomly when you add the?Container?widget.
And same thing will happen for the other child.
Now adding the keys to the level of the?Container?widget, this will solve our problem. This time, as flutter looks at one level of the tree at the time and notice where’s the problem, so everything works perfectly in this case and smoothly the swap happens same as previously happened.
Types of keys
Flutter has several different kinds of keys which can be used according the data we’re preserving.
Unique Keys
When you have a situation where you have collection of widgets and you want to ensure each widget must distinct from each other, You can use unique key.
The unique key is also used in our example app beacause we don’t have any constant data in our widgets. And while constructing widgets we didn’t know what the generated number would be.
Object Keys
When dealing with more complex combination of data, such as with a social media feed app where each post is represented by a Post object and users can like, comment, and share posts, it’s important to efficiently maintain the state of individual posts, and update the UI correctly when new posts are added, existing posts are modified, or the feed is refreshed.
In this scenario,?ObjectKey()?uniquely identifies each post in the feed, even if objects have the same description, number of likes, and other fields. The ObjectKey makes each post different from others.
Value Keys
It is useful when the identity of the object is not based on its memory reference like the?ObjectKey, but on a specific attribute or property of the object. for example in the Todo App, let’s say we want the text of todo item must be unique and different from others, so here we can use a?ValueKey?because the text is a value, an attribute of the Todo object.
Global Keys
The?GlobalKey?is a key that allows you to access the state and properties of a specific widget from anywhere in your application, not just its parent.
such as in cases involving form validation or managing state in a complex widget tree. We need a global key.
For example We want to perform form validation and display error messages for each field. To achieve this, we’ll use?GlobalKey?to access the form's state and validate its fields from different parts of the app.
PageStorage Keys
PageStorage Keys are the special keys that are used to persist the user scroll location.
Summary
The type of key that you use will depend on the specific situation. If you need to uniquely identify a widget and preserve its state, then you should use a?UniqueKey. If you need to uniquely identify a widget based on its object identity, then you should use an?ObjectKey. If you need to uniquely identify a widget based on its value, then you should use a?ValueKey. And if you need to uniquely identify a widget globally, then you should use a?GlobalKey. And finally use?PageStorageKey when you want to persist user scroll location in a scrollable view.
Learn everything about Flutter at →?https://flutter.dev
Let’s connect:
GitHub:?https://github.com/AdnanKhan45
Instagram:?https://www.instagram.com/dev.adnankhan/
YouTube:?https://youtube.com/@eTechViral