Skip to content

avoid_inherited_widget_in_initstate

v0.8.0WarningConfigurableState Management

This rule flags SomeInheritedWidget.of(context) and .maybeOf(context) calls inside a State’s initState method — including Theme.of, MediaQuery.of, Navigator.of, and any custom InheritedWidget.

Those lookups are backed by dependOnInheritedWidgetOfExactType, which is not valid during initState. At that point the element is not fully mounted, so the call either throws outright or silently registers a dependency that never delivers updates. Either way the widget will not rebuild when the theme, media query, or locale changes.

didChangeDependencies exists exactly for this: it runs once immediately after initState, and again every time an inherited dependency changes.

See also: State.initState docs | State.didChangeDependencies docs

Caching a theme colour at init time. It either throws immediately, or freezes at whatever the theme was when the widget was first inserted and ignores every later theme change:

class _BannerState extends State<Banner> {
late final Color _background;
@override
void initState() {
super.initState();
_background = Theme.of(context).primaryColor; // LINT
}
@override
Widget build(BuildContext context) => ColoredBox(color: _background);
}

Any inherited lookup has the same problem — MediaQuery.of, Navigator.of, Localizations.of, and your own InheritedWidget:

class _SplitViewState extends State<SplitView> {
late final bool _isWide;
@override
void initState() {
super.initState();
_isWide = MediaQuery.of(context).size.width > 600; // LINT
}
@override
Widget build(BuildContext context) =>
_isWide ? const Text('wide') : const Text('narrow');
}

Read it in build, where it re-reads on every rebuild:

class _BannerState extends State<Banner> {
@override
Widget build(BuildContext context) =>
ColoredBox(color: Theme.of(context).primaryColor);
}

When the value genuinely has to be cached — because computing it is expensive, or because something else needs it in a callback — use didChangeDependencies. Flutter calls it once right after initState, and again every time an inherited dependency changes:

class _SplitViewState extends State<SplitView> {
late bool _isWide;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_isWide = MediaQuery.of(context).size.width > 600;
}
@override
Widget build(BuildContext context) =>
_isWide ? const Text('wide') : const Text('narrow');
}

Note the field is late, not late final — didChangeDependencies runs more than once.

Only .of(context) and .maybeOf(context) on a type that is an InheritedWidget are matched. A helper that wraps the lookup — AppTheme.current(context) — is not recognised.

The check descends into closures declared inside initState, because a closure invoked synchronously fails the same way. A lookup inside a closure that deliberately escapes initState — one passed to WidgetsBinding.instance.addPostFrameCallback, say — runs after mounting and is safe, but is still reported. Suppress those with // ignore: many_lints/avoid_inherited_widget_in_initstate.

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

To turn it off:

many_lints.yaml
rules:
avoid_inherited_widget_in_initstate: false

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

Projects with a state abstraction that does not extend Flutter’s State can opt that base class into this rule:

analysis_options.yaml
many_lints:
rules:
avoid_inherited_widget_in_initstate:
state_base_classes: [AppState]
Option Type Default Description
state_base_classes list of strings [] Additional non-State base classes whose subclasses should be treated as state classes