Skip to content

always_pass_global_key

v1.0.0 Warning Widget Best Practices

This rule flags a GlobalKey constructed inside a build method. build runs on every rebuild, so the key gets a new identity each time and Flutter discards the entire subtree it identifies.

Flutter matches elements to widgets by key. A GlobalKey created in build is a different object on every rebuild, so the framework concludes the widget is new: it unmounts the old element, disposes its State, and builds a fresh one.

Everything held in that subtree goes with it — form contents, scroll position, animation controllers, focus. The symptom is a form that clears itself or a list that jumps to the top whenever anything unrelated triggers a rebuild. Nothing throws, so it reads as a mysterious UI bug rather than a lifetime mistake.

A GlobalKey is meant to be long-lived: created once, stored in a State field, and reused across rebuilds.

See also: Flutter: GlobalKey, When to use keys

class MyForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
final key = GlobalKey<FormState>(); // new identity every rebuild
return Form(key: key, child: const SizedBox());
}
}

Hold the key in a State field so it survives rebuilds:

class _MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(key: _formKey, child: const SizedBox());
}
}

Note this also means the widget must be stateful — a StatelessWidget has nowhere to keep a key that outlives a rebuild.

Only construction inside a method named build is reported. A GlobalKey created in a helper method that build calls is not detected, though it has the same problem.

LocalKey subclasses such as ValueKey and ObjectKey are not reported. They are compared by value, not identity, so creating one in build is normal and correct.

This rule is in the core preset, so it is on with preset: core, preset: recommended or preset: opinionated.

To turn it off:

many_lints.yaml
rules:
always_pass_global_key: false

To keep the rule on but skip certain paths, use per-rule exclude.