Skip to content

InheritedWidget

Passing data down a deep tree by hand — through every constructor — is miserable. This is “prop drilling,” and it couples every intermediate widget to data it does not use.

InheritedWidget is Flutter’s built-in answer. You place one high in the tree, and any descendant can read it directly via context, no matter how deep, without a single widget in between knowing about it. Theme, MediaQuery, Navigator, and Provider are all InheritedWidgets — you have been using it all along.

A descendant calls dependOnInheritedWidgetOfExactType, usually wrapped in a static of(context) method by convention:

class ThemeConfig extends InheritedWidget {
const ThemeConfig({super.key, required this.color, required super.child});
final Color color;
// Convention: a static `of` that registers the caller as a dependent.
static ThemeConfig of(BuildContext context) {
final result =
context.dependOnInheritedWidgetOfExactType<ThemeConfig>();
assert(result != null, 'No ThemeConfig found in context');
return result!;
}
// Called when a new ThemeConfig replaces the old one: should dependents rebuild?
@override
bool updateShouldNotify(ThemeConfig oldWidget) => color != oldWidget.color;
}
// Anywhere below it:
final color = ThemeConfig.of(context).color;

Two mechanics matter here. dependOnInheritedWidgetOfExactType does two things at once: it finds the nearest ancestor of that type (a fast, cached lookup up the element tree), and it registers the calling element as a dependent. And updateShouldNotify decides, when the inherited widget is rebuilt with new data, whether dependents actually need to rebuild.

This is the payoff. When the InheritedWidget updates and updateShouldNotify returns true, Flutter rebuilds only the widgets that called of(context) — not the whole subtree. Widgets in between that never depended on it are untouched.

flowchart TB
  iw["InheritedWidget
(color changes)"] --> a["Widget A
(did not depend)
NOT rebuilt"]
  iw --> b["Widget B
called of(context)
REBUILT"]
  iw --> c["Widget C
(did not depend)
NOT rebuilt"]
Only registered dependents rebuild on update

That targeted rebuild is why InheritedWidget scales: reading is a cheap upward lookup, and updates only ripple to true dependents. Every higher-level state solution is, underneath, this mechanism plus ergonomics.

What problem does InheritedWidget solve?
What does dependOnInheritedWidgetOfExactType do?
When an InheritedWidget updates, which widgets rebuild?
What is the role of updateShouldNotify?