What is InheritedWidget?
3 min read
State Management
| Concept | What it does |
|---|---|
Subclass InheritedWidget | Holds the data you want to expose |
static of(context) | Sugar for context.dependOnInheritedWidgetOfExactType<T>() |
updateShouldNotify(old) | When you return true, dependent widgets are marked dirty |
| Dependency registration | Calling of(context) registers the calling element as a dependent — only those rebuild |
That last point is the magic: thousands of widgets can sit below your InheritedWidget, but only the ones that called of(context) rebuild when the data changes.
Code in action
class AppTheme extends InheritedWidget {
const AppTheme({
super.key,
required this.colors,
required super.child,
});
final ColorScheme colors;
static AppTheme of(BuildContext context) {
final w = context.dependOnInheritedWidgetOfExactType<AppTheme>();
assert(w != null, 'No AppTheme in tree');
return w!;
}
@override
bool updateShouldNotify(AppTheme old) => colors != old.colors;
}
// Provide
AppTheme(
colors: ColorScheme.fromSeed(seedColor: Colors.indigo),
child: MaterialApp(home: const Home()),
);
// Consume — only widgets that call .of() rebuild on change
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = AppTheme.of(context).colors;
return Container(color: colors.primary);
}
}
Where Flutter already uses it
| API | Backing widget |
|---|---|
Theme.of(context) | _InheritedTheme |
MediaQuery.of(context) | _MediaQueryFromView |
Navigator.of(context) | inherited element of _NavigatorScope |
Scaffold.of(context) | _ScaffoldScope |
Provider.of(context) / context.watch | InheritedProvider |
Riverpod's ProviderScope | _ProviderScope (inherited) |
So when you ask "should I use InheritedWidget or Provider?" — Provider is just an ergonomic wrapper. Use Provider/Riverpod by default; reach for raw InheritedWidget when you need a tiny dependency without adding a package.
When to write your own vs use a library
| Situation | Choice |
|---|---|
| Quick, single-purpose value (theme override, locale) | Custom InheritedWidget |
| Mutable data, change notifications, lifecycle | Provider / Riverpod (built on it) |
| Public package API where users shouldn't add a dep | Custom InheritedWidget |
| You're already using a state-management lib | Use the lib's APIs, not raw inherited widgets |
Common mistakes to avoid
// ❌ Reading without registering — won't rebuild on changes
context.getInheritedWidgetOfExactType<AppTheme>();
// ✅ Use dependOnInheritedWidgetOfExactType (.of helpers do this)
// ❌ updateShouldNotify always returning true
@override
bool updateShouldNotify(_) => true; // unnecessary rebuilds
// ✅ Compare fields properly
// ❌ Storing mutable state directly in an InheritedWidget
// InheritedWidgets are rebuilt on every parent rebuild. Wrap with an
// InheritedNotifier or a StatefulWidget that holds the state.
// ❌ Forgetting that .of(context) returns the NEAREST ancestor
// Two nested AppThemes → the inner one wins for the inner subtree
// ❌ Using context.read inside build for InheritedWidget data
// Subscribe with .of() (or context.watch in Provider) so rebuilds happen.
Interview follow-ups
-
Why is
InheritedWidgetfaster than passing data via constructors? It's not "faster" — constructor passing is O(1). The win is scope: descendants can read the value without you threading it through every layer, and Flutter only rebuilds the specific widgets that depend on it, not the whole subtree. -
What's
updateShouldNotifyand what happens if you return true unnecessarily? It tells Flutter whether dependents need to rebuild. Returning true when nothing changed forces wasted rebuilds in every descendant that calledof(context). Compare meaningfully — preferably with equatable fields or value semantics. -
What's the difference between
dependOnInheritedWidgetOfExactTypeandgetInheritedWidgetOfExactType?depend…registers the calling element as a dependent — it'll rebuild when the inherited widget changes.get…reads it once without subscribing. Usedepend(or.ofhelpers) for values that can change;getonly for one-shot reads (e.g., duringinitState). -
How is Provider built on top of
InheritedWidget? Provider wraps anInheritedWidget(actuallyInheritedProvider) plus aChangeNotifier/Listenableand exposescontext.watch,context.read,context.selecthelpers. The underlying dispatch mechanism — registering dependents and selectively rebuilding — is exactly theInheritedWidgetmachinery.
How helpful was this content?
Please sign in to rate this article.