always_pass_global_key
v1.0.0 Warning Widget Best Practices
Flags a GlobalKey constructed inside a build method.
build runs on every rebuild, so the key gets a new identity each time. Flutter matches elements by key, concludes the widget is new, and discards the whole subtree it identifies — form contents, scroll position, animation controllers, focus. Nothing throws; the symptom is a form that clears itself when something unrelated rebuilds.
This rule is in the core preset, so it is on with preset: core and every preset above it. No configuration.
See also: Flutter: GlobalKey
class MyForm extends StatelessWidget { const MyForm({super.key});
@override Widget build(BuildContext context) { // New identity every rebuild — the Form's state is thrown away final formKey = GlobalKey<FormState>(); return Form(key: formKey, child: const SizedBox()); }}Hold the key in a State field, so it is created once and survives rebuilds. This also means the widget has to be stateful — a StatelessWidget has nowhere to keep a key that outlives a rebuild.
class MyForm extends StatefulWidget { const MyForm({super.key});
@override State<MyForm> createState() => _MyFormState();}
class _MyFormState extends State<MyForm> { final _formKey = GlobalKey<FormState>();
@override Widget build(BuildContext context) { return Form(key: _formKey, child: const SizedBox()); }}Local keys are fine
Section titled “Local keys are fine”ValueKey, ObjectKey and the other LocalKey subclasses are compared by value, not identity, so creating one in build is normal and never reported:
@overrideWidget build(BuildContext context) { return Column( children: [ for (final item in items) Text(item, key: ValueKey(item)), ], );}Known limitations
Section titled “Known limitations”Only construction inside a method literally named build is reported. A GlobalKey created in a helper that build calls has the same problem and is not detected:
// Not reported, but just as brokenWidget _field() => Form(key: GlobalKey<FormState>(), child: const SizedBox());Turning this rule off
Section titled “Turning this rule 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.