Flutter Daily: Understanding Keys - When and Why to Use Them
Understanding Keys - When and Why to Use 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’re reordering lists or swapping widgets, Keys become essential for maintaining correct state and avoiding UI bugs.
The Problem Keys Solve
Imagine you have a list of stateful widgets (like checkboxes or text fields). When you reorder them, Flutter might reuse the wrong state for the wrong widget because it matches widgets by type and position, not by identity. Keys tell Flutter “this specific widget is the same one, even if it moved.”
Example: List Reordering Without Keys (Broken)
class ColoredBoxList extends StatefulWidget {
@override
_ColoredBoxListState createState() => _ColoredBoxListState();
}
class _ColoredBoxListState extends State<ColoredBoxList> {
List<Widget> boxes = [
ColorBox(color: Colors.red),
ColorBox(color: Colors.blue),
ColorBox(color: Colors.green),
];
void reorderBoxes() {
setState(() {
final first = boxes.removeAt(0);
boxes.add(first);
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
...boxes,
ElevatedButton(
onPressed: reorderBoxes,
child: Text('Reorder'),
),
],
);
}
}
class ColorBox extends StatefulWidget {
final Color color;
const ColorBox({required this.color});
@override
_ColorBoxState createState() => _ColorBoxState();
}
class _ColorBoxState extends State<ColorBox> {
bool selected = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => selected = !selected),
child: Container(
width: 100,
height: 100,
color: selected ? widget.color.withOpacity(0.5) : widget.color,
),
);
}
}
Problem: When you tap a box to select it, then reorder, the selection state stays in the wrong box because Flutter reuses the state based on position!
Example: Fixed with Keys
class _ColoredBoxListState extends State<ColoredBoxList> {
List<Widget> boxes = [
ColorBox(key: ValueKey('red'), color: Colors.red),
ColorBox(key: ValueKey('blue'), color: Colors.blue),
ColorBox(key: ValueKey('green'), color: Colors.green),
];
void reorderBoxes() {
setState(() {
final first = boxes.removeAt(0);
boxes.add(first);
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
...boxes,
ElevatedButton(
onPressed: reorderBoxes,
child: Text('Reorder'),
),
],
);
}
}
Fixed! Now each ColorBox has a unique ValueKey, so Flutter knows which widget is which even after reordering, and the selected state follows the correct box.
Types of Keys
- ValueKey: Use when each widget has a unique value (ID, name, etc.)
- ObjectKey: Use when comparing objects (less common)
- UniqueKey: Generates a unique key (use sparingly, creates new key every build)
- GlobalKey: Access widget state from anywhere (use rarely, has performance cost)
Try It Yourself
Create a simple todo list where each item has a checkbox. Add the ability to reorder items. First implement it WITHOUT keys and tap some checkboxes, then reorder—you’ll see the checked state doesn’t follow the items. Then add ValueKey using the todo item’s ID or text, and watch the state correctly follow items when reordered.
Tip of the Day
When do you need Keys? Only when you’re:
- Reordering a list of stateful widgets
- Adding/removing stateful widgets in a list
- Swapping widgets at the same level in the tree
For stateless widgets or widgets that never reorder, you don’t need Keys. Use them sparingly—they’re tools for specific situations, not something to add everywhere “just in case.”