In Flutter, keys are used to control the identity of widgets in the widget tree, especially when the widget tree changes or gets rebuilt. They play a crucial role in preserving the state of widgets, managing reorderable lists, and improving the efficiency of the framework’s diffing algorithm.
Why Do We Need Keys in Flutter?
When the Flutter framework rebuilds the widget tree (usually due to changes in state), it needs to match the new widget configuration with the previous one. Without keys, Flutter tries to match widgets based on their runtime type and position in the tree. This works in many cases, but when widgets swap positions or change order (like in lists or complex UI structures), this can cause unwanted behaviors, such as losing state or misaligned UI elements.
Keys help Flutter uniquely identify widgets, ensuring that the correct state and properties are maintained, even if the structure of the widget tree changes.
Types of Keys in Flutter
- ValueKey
- A ValueKey is a key that uses a specific value to identify the widget.
- This value can be anything that is unique, such as a string, number, or object.
1ValueKey('someUniqueValue'); - ObjectKey
- An ObjectKey uses an object’s identity (its memory address) to identify the widget. It’s useful when you want to use an object itself as a unique identifier.
1ObjectKey(someObject); - UniqueKey
- A UniqueKey is automatically generated and always unique. Each time a
UniqueKey
is created, it represents a new instance. It’s useful when you want to force Flutter to treat widgets as distinct, even if they have the same runtime type and position in the tree.
1UniqueKey(); - A UniqueKey is automatically generated and always unique. Each time a
- GlobalKey
- A GlobalKey is a powerful key that provides access to the widget, its state, and its context. It is often used to persist the state across the entire widget tree or to access a widget from anywhere in the app.
- It is useful for widgets that need to maintain their state or need to be accessed from multiple parts of the app.
1final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Common Use Cases for Keys
1. Preserving State in a List
When you have a list of stateful widgets that may change position, adding a key helps ensure that Flutter preserves the state of each widget during reordering. Without a key, Flutter might confuse the widgets, causing them to lose their state.
Example:
1 2 3 4 5 6 7 8 |
ListView.builder( itemBuilder: (context, index) { return ListTile( key: ValueKey(items[index].id), // Unique key based on item ID title: Text(items[index].name), ); }, ); |
2. Reordering Widgets
Keys are essential when dealing with reorderable or draggable lists to identify and preserve the correct items during the reordering process.
Example:
1 2 3 4 5 6 7 |
ReorderableListView( onReorder: _onReorder, children: items.map((item) => ListTile( key: ValueKey(item.id), // Ensures correct items are reordered title: Text(item.name), )).toList(), ); |
3. Managing Stateful Widgets
When a widget needs to maintain its state even after its position in the tree changes, a key ensures that the widget is correctly matched and does not lose its state.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class MyStatefulWidget extends StatefulWidget { MyStatefulWidget({Key? key}) : super(key: key); @override _MyStatefulWidgetState createState() => _MyStatefulWidgetState(); } class _MyStatefulWidgetState extends State<MyStatefulWidget> { int counter = 0; @override Widget build(BuildContext context) { return GestureDetector( onTap: () => setState(() => counter++), child: Text('Counter: $counter'), ); } } |
Without a key, if the position of this widget changes within its parent, Flutter might rebuild it and reset the counter. With a key, Flutter knows which widget instance to associate the state with.
4. Using GlobalKey for Access to State
GlobalKey is used when you need access to the state of a widget or to perform certain operations on the widget. It’s commonly used in form validation or navigating between widgets.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); Form( key: _formKey, child: Column( children: <Widget>[ TextFormField( validator: (value) { if (value == null || value.isEmpty) { return 'Please enter some text'; } return null; }, ), ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { // Form is valid, proceed } }, child: Text('Submit'), ), ], ), ); |
In this example, the GlobalKey allows you to access the form’s state from outside the Form
widget and validate its fields.
When to Use Keys
- When working with lists of widgets (especially stateful widgets) that might change order, like in
ListView
,GridView
, or reorderable lists. - When widgets are swapped, added, or removed in a dynamic UI, and you want Flutter to keep track of which widget is which.
- When accessing widget state from outside the widget tree using
GlobalKey
.
Conclusion
Keys are essential in Flutter for managing the identity of widgets and preserving their state across widget tree rebuilds. They help Flutter efficiently match widgets during updates and ensure correct behavior when dealing with dynamic or reorderable UIs. Proper use of keys can greatly enhance the stability and performance of your Flutter application.