Understanding Flutter Keys: When and Why to Use Them

Understanding Flutter 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 do, they’re essential for preventing bugs and unexpected behavior.

Keys tell Flutter how to match old widgets with new widgets during rebuilds. Without proper keys, Flutter might reuse state incorrectly, leading to UI glitches.

When Do You Need Keys?

You need keys when:

  1. Reordering a list of stateful widgets
  2. Adding or removing widgets from a collection
  3. Changing the type of widget at a specific position

Example: The Problem Without Keys

class ItemList extends StatefulWidget {
  @override
  _ItemListState createState() => _ItemListState();
}

class _ItemListState extends State<ItemList> {
  List<Widget> items = [
    ColoredBox(color: Colors.red),
    ColoredBox(color: Colors.blue),
    ColoredBox(color: Colors.green),
  ];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ...items,
        ElevatedButton(
          onPressed: () {
            setState(() {
              // Remove first item
              items.removeAt(0);
            });
          },
          child: Text('Remove First'),
        ),
      ],
    );
  }
}

class ColoredBox extends StatefulWidget {
  final Color color;
  ColoredBox({required this.color});

  @override
  _ColoredBoxState createState() => _ColoredBoxState();
}

class _ColoredBoxState extends State<ColoredBox> {
  late Color displayColor;

  @override
  void initState() {
    super.initState();
    displayColor = widget.color;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: displayColor,
    );
  }
}

When you remove the first item, you might see unexpected colors because Flutter reuses state incorrectly.

Solution: Adding Keys

class _ItemListState extends State<ItemList> {
  List<Widget> items = [
    ColoredBox(key: UniqueKey(), color: Colors.red),
    ColoredBox(key: UniqueKey(), color: Colors.blue),
    ColoredBox(key: UniqueKey(), color: Colors.green),
  ];

  // ... rest stays the same
}

Types of Keys

ValueKey: Use when widgets can be uniquely identified by a value (like an ID)

ValueKey(item.id)

ObjectKey: Use when you have a unique object

ObjectKey(myObject)

UniqueKey: Creates a unique key each time (use sparingly)

UniqueKey()

GlobalKey: Use when you need to access widget state or context from anywhere (rare use case)

final GlobalKey<FormState> formKey = GlobalKey<FormState>();

Try It Yourself

Create a simple todo list where items can be reordered. First build it without keys and observe the behavior. Then add ValueKeys using the todo item’s text or ID. Notice how state preservation changes.

Tip of the Day

Use ValueKey with stable identifiers (like database IDs) rather than UniqueKey for list items. UniqueKey creates a new key on every rebuild, which defeats the purpose. For most list cases, ValueKey(item.id) is your best choice.