use_closest_build_context
v0.4.0 Warning Fix Widget Best Practices
Flags a reference to an outer BuildContext inside a nested callback that has its own.
Theme.of, MediaQuery.of and Navigator.of all walk up from the element they are handed. A Builder exists precisely to introduce a new element below the widgets around it, so reaching past its context to the enclosing build’s one resolves the lookup against a different subtree — skipping whatever the Builder was there to see. The code compiles and usually looks right. The quick fix rewrites the reference to the inner name.
This rule is in the recommended preset, so it is on with preset: recommended and every preset above it. No configuration.
See also: BuildContext
class OrderPage extends StatelessWidget { const OrderPage({super.key});
@override Widget build(BuildContext context) { return Builder( builder: (_) { // Uses the outer context instead of the Builder's own return _label(context); }, ); }
Widget _label(BuildContext context) => const Text('Order');}class OrderPage extends StatelessWidget { const OrderPage({super.key});
@override Widget build(BuildContext context) { return Builder( builder: (innerContext) { // Uses the Builder's own context return _label(innerContext); }, ); }
Widget _label(BuildContext context) => const Text('Order');}Why it matters: the Scaffold case
Section titled “Why it matters: the Scaffold case”The commonest real bug this catches is ScaffoldMessenger.of(context) reaching above the Scaffold that was just built:
// Don't — the outer context is above the Scaffold, so the lookup throws@overrideWidget build(BuildContext context) { return Scaffold( body: Builder( builder: (inner) => TextButton( onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Hi'))), child: const Text('Show'), ), ), );}
// Do — the Builder's context sits below the Scaffold@overrideWidget build(BuildContext context) { return Scaffold( body: Builder( builder: (inner) => TextButton( onPressed: () => ScaffoldMessenger.of(inner).showSnackBar(const SnackBar(content: Text('Hi'))), child: const Text('Show'), ), ), );}Known limitations
Section titled “Known limitations”Shadowing is not reported. When the inner parameter is also called context, the outer one is simply out of scope and the reference already resolves to the closest context. That is the reason the idiomatic spelling — builder: (context) => ... — never trips this rule.
Only methods are examined. The rule starts from a method with a BuildContext parameter, so an outer context captured by a top-level function or a field initialiser is not tracked.
Only an exact BuildContext parameter counts on both sides — a callback whose parameter is a subclass is not treated as providing a closer context.
Turning this rule off
Section titled “Turning this rule off”To turn it off:
rules: use_closest_build_context: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”never_discard_build_context— Don’t discard a BuildContext parameter with a wildcard.avoid_build_context_in_providers— Providers outlive widgets, so they should not receive a BuildContext.avoid_passing_build_context_to_blocs— Prevent passing BuildContext to Bloc or Cubit classes.avoid_too_many_widgets_per_build— Keep one build method within a widget budget.