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.
Why use this rule
Section titled “Why use this rule”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.
Known limitations
Section titled “Known limitations”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.
Configuration
Section titled “Configuration”This rule is in the core preset, so it is on with preset: core,
preset: recommended or preset: opinionated.
To turn it off:
rules: always_pass_global_key: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”pass_existing_future_to_future_builder— Don’t create a new Future inline inside FutureBuilder.pass_existing_stream_to_stream_builder— Don’t create a new Stream inline inside StreamBuilder.avoid_conditional_hooks— Never call hooks inside conditionals, loops, or ternaries.avoid_deep_widget_nesting— Keep a widget tree within a nesting budget.