Flutter Keys: When and Why You Need Them
Flutter Keys: When and Why You Need Them
What You’ll Learn
Keys are Flutter’s way of preserving state when widgets move around in the widget tree. Most of the time you don’t need them—but when you do, they’re essential for preventing weird bugs where your UI state gets confused or lost.
The Problem Keys Solve
Flutter identifies widgets by their position in the tree and their type. When you reorder widgets (like swapping list items), Flutter might reuse the wrong state because it only checks type and position, not identity.
Here’s a classic example that breaks without keys:
class ColoredBox extends StatefulWidget {
final Color color;
ColoredBox(this.color);
@override
_ColoredBoxState createState() => _ColoredBoxState();
}
class _ColoredBoxState extends State<ColoredBox> {
bool _isFavorite = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _isFavorite = !_isFavorite),
child: Container(
width: 100,
height: 100,
color: widget.color,
child: _isFavorite ? Icon(Icons.star) : null,
),
);
}
}
class SwappableBoxes extends StatefulWidget {
@override
_SwappableBoxesState createState() => _SwappableBoxesState();
}
class _SwappableBoxesState extends State<SwappableBoxes> {
List<Widget> boxes = [
ColoredBox(Colors.red),
ColoredBox(Colors.blue),
];
void _swap() {
setState(() {
boxes = boxes.reversed.toList();
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(children: boxes),
ElevatedButton(
onPressed: _swap,
child: Text('Swap'),
),
],
);
}
}
The bug: Tap the red box to favorite it, then hit swap. The star stays in the first position instead of moving with the red box. Flutter reused the state for position 0 and 1, ignoring which color is which.
The Fix: Add Keys
class SwappableBoxes extends StatefulWidget {
@override
_SwappableBoxesState createState() => _SwappableBoxesState();
}
class _SwappableBoxesState extends State<SwappableBoxes> {
List<Widget> boxes = [
ColoredBox(Colors.red, key: UniqueKey()), // Add unique keys
ColoredBox(Colors.blue, key: UniqueKey()),
];
// rest of the code stays the same...
}
Now Flutter tracks each widget by its unique key, so state moves with the correct widget.
Types of Keys You’ll Use
ValueKey: When each widget has a unique data value
ListView.builder(
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
key: ValueKey(item.id), // Use the item's ID
title: Text(item.name),
);
},
)
ObjectKey: When the entire object is unique
key: ObjectKey(userProfile)
UniqueKey: When you just need a unique identity (generates random key each time)
key: UniqueKey()
GlobalKey: When you need to access widget state from outside its tree (use sparingly—expensive)
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: // form fields,
)
// Later, access from anywhere:
_formKey.currentState?.validate();
When to Use Keys
- Reorderable lists (drag-to-reorder)
- Lists where items can be removed from any position
- Animating widgets in and out
- Preserving scroll position in complex navigation
- When you need to access a widget’s state from outside (GlobalKey only)
When You Don’t Need Keys
- Static lists that never change order
- Simple widgets that rebuild from data (stateless or state derived from props)
- Most of your app—keys are the exception, not the rule
Try It Yourself
Build a simple todo list where you can remove items by swiping. Try it without keys first—you’ll see removed items disappear but checkmarks stay in the wrong positions. Then add ValueKey(todo.id) to each item and watch the bug disappear.
Tip of the Day
If you’re seeing weird state bugs where UI elements seem to “stick” to the wrong widgets after reordering or filtering lists, keys are probably the solution. Add a print() statement in your widget’s initState() to see when Flutter is creating new widgets vs. reusing old ones—this helps you understand when keys are needed.