What are Keys in Flutter and when should you use them?

High PriorityAsked in ~70% of Flutter interviews

3 min read

Flutter Internals

Flutter's diff algorithm compares old and new widget trees at each position. By default:

Before:  [A] [B] [C]                  After removing B:  [A] [C]
          ↑                                                ↑
         pos 0    1   2                                   pos 0   1

Without keys, Flutter sees "position 1 used to be B, now it's C" — it doesn't drop B, it rewrites it into C, keeping B's underlying State attached to the new widget. For stateless widgets that's harmless; for stateful ones (checkboxes, text fields, animations), state ends up on the wrong row.

With keys, Flutter matches by identity:

Before:  [A:1] [B:2] [C:3]            After:  [A:1] [C:3]

Now B is correctly disposed and C keeps its state.


The 4 key types

KeyIdentity is…Use for
ValueKey(x)== on the valueMost lists — pass a stable item id
ObjectKey(o)identity (identical) of the objectWhen values can be equal but instances differ
UniqueKey()unique every buildForce a widget to rebuild from scratch
GlobalKey<T>unique app-wideAccess a child's state or RenderObject from outside

Code in action

// 1️⃣ Stateful list items — keys preserve state on reorder/remove
ListView(
  children: users.map((u) => CheckboxTile(
    key: ValueKey(u.id),
    user: u,
  )).toList(),
);
 
// 2️⃣ Force a clean slate (reset a form when filters change)
RefreshableProductList(key: ValueKey(filter));   // new key → new state
 
// 3️⃣ Global key — read/control child state
final _formKey = GlobalKey<FormState>();
Form(key: _formKey, child: ...);
if (_formKey.currentState!.validate()) { ... }
 
// 4️⃣ UniqueKey — when you specifically want a remount
AnimatedSwitcher(
  child: Container(key: UniqueKey(), color: bgColor),
);

When to add a key

SituationKey?
List of stateless display items (Text)❌ Not needed
List of stateful items that can reorder/insert/removeValueKey(id)
Swapping between two widgets of the same type✅ Distinct keys to force a remount
Reading a child widget's state from outsideGlobalKey<T>
Forcing a remount when params changeValueKey(param)
Every widget "just to be safe"❌ Overhead with no benefit

Common mistakes to avoid

// ❌ No keys on stateful list items — state attaches to wrong row
Column(children: items.map((i) => CheckboxTile(item: i)).toList());
 
// ✅ Stable ValueKey
Column(children: items.map((i) => CheckboxTile(
  key: ValueKey(i.id),
  item: i,
)).toList());
 
// ❌ Using UniqueKey() in a hot path — forces full remount every build
return ListView(
  children: items.map((i) => Tile(key: UniqueKey(), item: i)).toList(),
);
// ✅ Use stable IDs unless you actually want a remount
 
// ❌ Using `i` (index) as the key in a reorderable list
key: ValueKey(index),   // shifts when you remove/reorder → defeats the purpose
 
// ❌ Over-using GlobalKey — they're more expensive and lifecycle-tricky
GlobalKey here, GlobalKey there → state lookups everywhere
// ✅ Prefer callbacks / lifting state; GlobalKey only when you truly need cross-tree access
 
// ❌ Putting a key on the wrong widget
// Keys go on the widget WHOSE IDENTITY matters across rebuilds —
// usually the top widget you return for each item, not the inner Text.

Interview follow-ups

  1. What's the difference between ValueKey and ObjectKey? ValueKey(v) uses == on the wrapped value — perfect for stable IDs. ObjectKey(o) uses identical() — useful when two model instances have equal fields but aren't the same object, and you want them treated as distinct.

  2. What does a GlobalKey do, and what are the trade-offs? It gives the widget a unique identity across the whole tree, so its element can move without being recreated, and you can read currentState / currentContext / currentWidget from anywhere. Trade-offs: more memory, gotchas around moving in/out of the tree, and easy to overuse.

  3. Why does using index as a key in a dynamic list break state preservation? Because the index shifts when items are added/removed/reordered. Item at index 2 might become index 1 after a delete — same key, different item, same broken matching you get with no key.


How helpful was this content?

Please sign in to rate this article.