Unlocking Your Flutter Widgets With Keys
Learn how using the right keys in your Flutter widgets can help you avoid UI bugs and improve the performance of your app. By Michael Malak.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Unlocking Your Flutter Widgets With Keys
25 mins
- Getting Started
- Setting up the Starter Project
- Understanding Keys
- Making Swaps With Stateless Widgets
- Handling Swaps in Stateful Widgets
- Using Keys to Avoid Unexpected Results
- Reordering TODOs
- Enabling Drag and Drop
- Adding a Key
- Using Global Keys
- Adding a TODO
- Adding a TODO to the List
- Preserving the Scroll Position
- Preserving the News State
- Refetching News Articles
- Fixing a Bug in the State
- Adding Dividers
- Preserving the Expanded State of the News Items
- Where to Go From Here?
Flutter commonly uses keys when it needs to uniquely identify specific widgets within a collection. Using keys also helps Flutter preserve the state of StatefulWidget
s while they’re being replaced with other widgets or just moved in the widget tree. Almost all Flutter widgets accept keys as optional parameters in their constructors.
Have you wondered when to pass a key and what happens under the hood? In this tutorial, you’ll unlock that mystery as you build a simple app to manage a TODO list and display news headlines.
By the end of this tutorial, you’ll learn:
- What keys are and how they work.
- When to use a key.
- How to work with different types of keys.
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.
This tutorial uses Android Studio 4.1. Some of the screenshots are specific to it, but you can follow along with Visual Studio Code or IntelliJ as well.
You’ll work on The Morning App, a single-page app that displays a TODO list on one tab and a list of news articles on another. Here’s what you want to be able do with each page:
- Todos: Add a new TODO, mark a TODO as done or delete a TODO.
- News: View the latest news articles from HackerNews and tap an article to view some of its metadata.
Here’s how the pages will look when you’re done:
Now, it’s time to take a look at the project.
Setting up the Starter Project
The starter project already contains the logic to fetch articles from HackerNews, save TODOs to the cache and read TODOs from the cache.
Open Android Studio and choose Open an Existing Project. Then, choose the starter folder from the downloaded materials.
Fetch the dependencies declared in pubspec.yaml by clicking Pub get at the top of the pane when you’re in this file.
For this tutorial, the most important files in the project are:
- lib/ui/home/home_page.dart: The main page of the app that displays the two tabs for displaying TODOs and news articles.
- lib/ui/todos/todos_page.dart: The widget class of the page associated with the Todos tab.
- lib/ui/news/news_page.dart: The widget class of the page associated with the News tab.
- lib/ui/todos/add_todo_widget.dart: The widget class representing the bottom sheet, where the user can add a new TODO on the Todos page.
Build and run. The app launches with the Todos tab selected.
Now that you know what the starter project contains, you’ll take a deeper look at what keys are and why you use them.
Understanding Keys
Every Flutter widget can have a key, but adding them isn’t always useful. Here’s the key to understanding keys:
- Multiple widgets of the same type and at the same level in a widget tree may not update as expected unless they have unique keys, given that these widgets hold some state.
- Explicitly setting a key to a widget helps Flutter understand which widget it needs to update when state changes.
- Among other things, keys also store and restore the current scroll position in a list of widgets.
Consider an example to understand this better:
When Flutter lays out the widget tree, it builds a corresponding element tree. Internally, it maps each widget in the widget tree to an element in the element tree. The widget tree contains information about the UI and the element tree holds information about the structure of the app — meaning that each element holds details about:
- The
runtimeType
of the corresponding widget in the widget tree. - The reference to the corresponding widget in the widget tree.
- The reference to its child
Element
.
You can extract the rest of the information from the reference to the widget tree that each element holds.
Making Swaps With Stateless Widgets
Every change to the UI in a Flutter app is a result of triggering the build method. During this process, Flutter checks if the element tree is the same as the corresponding widget tree. Flutter makes this comparison starting from the parent widget, then proceeding to its children widgets.
StatelessWidget
s have no keys. Therefore, if the element has the same type as the corresponding new widget, the element updates its reference to point to the new widget and drops the reference to the old widget.
Handling Swaps in Stateful Widgets
In the case of StatefulWidget
s, however, an element stores a reference to the state of a widget — for example, State
— as well.
Therefore, the new widget could have the same runtime type as the old widget, but a different state. Based on the logic above, Flutter would update the reference of the widget in the element to point to the new widget but the element would still hold a reference to the state from the old widget. That’s a problem.
Using Keys to Avoid Unexpected Results
Adding a key to a widget that holds a reference to the state allows Flutter to make an additional comparison beyond the type of the widget. This ensures that when the types match but the keys don’t, Flutter forces the elements to drop to their widget reference and hold references to widgets where both the type and key match. This ensures that both the widget and state references update correctly.
You’ll see this in action later in the tutorial.
Now that you understand some theory behind using keys, it’s time to put that information to work by adding some new features to The Morning App.
Reordering TODOs
At this point, the starter code displays the TODOs. Your first goal is to give the users the ability to sort the TODO items by dragging and dropping them to new positions in the list.
In lib/ui/todos/todos_page.dart, replace //TODO: Reorder To-dos
with:
// 1
void reorderTodos(int oldIndex, int newIndex) {
// 2
if (oldIndex < newIndex) {
newIndex -= 1;
}
// 3
final item = todos.removeAt(oldIndex);
setState(() {
todos.insert(newIndex, item);
});
}
Here's what you did:
- You added a function to reorder the TODOs. That function takes two indices as parameters:
oldIndex
is the index of the TODO whose position will change andnewIndex
is the new index where you'll place the TODO. - Since you're going to remove the TODO from the old index then insert it into the new one, you subtracted 1 from
newIndex
in case it's afteroldIndex
. - You removed the element at
oldIndex
and inserted it intonewIndex
. You then calledsetState
so the UI reflects the changes.
Now that you've made the TODO items sortable, it's time to add the ability to drag and drop them.