In Flutter, what problem does an InheritedWidget solve, and how do you declare one so descendants can read its data?
answer
- avoid threading a value through constructors
- subclass with a final field
- static of and maybeOf
- dependOnInheritedWidgetOfExactType registers
- updateShouldNotify compares old widget
basics
~20 sAn InheritedWidget makes a value available to every descendant without passing it through each constructor. You subclass it with final fields, add static of/maybeOf methods that register a dependency, and override updateShouldNotify so only registered dependents rebuild when the value changes.
solid answer
~40 sIt solves **prop drilling**: a value such as the app's unit system (metric or imperial) is needed deep in the tree, and threading it through every intermediate constructor couples widgets that never use it. You declare a subclass of `InheritedWidget` with `final` fields and a `child`, plus the conventional static `of(context)` / `maybeOf(context)` methods that call `context.dependOnInheritedWidgetOfExactType<T>()`. That call both returns the nearest ancestor of exactly that type and **registers** the calling element as a dependent. You override `updateShouldNotify(oldWidget)` to say whether a new instance carries changed data; when it returns `true`, the framework rebuilds only the registered dependents, not the whole subtree. The widget itself is immutable, so the value changes when something above it (usually a `StatefulWidget`) rebuilds it with new data.
code
dart · 36 linesimport 'package:flutter/widgets.dart';
enum UnitSystem { metric, imperial }
class UnitsScope extends InheritedWidget {
const UnitsScope({super.key, required this.units, required super.child});
final UnitSystem units;
static UnitSystem? maybeOf(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<UnitsScope>()?.units;
static UnitSystem of(BuildContext context) {
final UnitSystem? units = maybeOf(context);
assert(units != null, 'No UnitsScope found in context');
return units!;
}
@override
bool updateShouldNotify(UnitsScope oldWidget) => units != oldWidget.units;
}
class TrailDistance extends StatelessWidget {
const TrailDistance({super.key, required this.km});
final double km;
@override
Widget build(BuildContext context) {
final UnitSystem units = UnitsScope.of(context);
return Text(switch (units) {
UnitSystem.metric => '${km.toStringAsFixed(1)} km',
UnitSystem.imperial => '${(km * 0.621371).toStringAsFixed(1)} mi',
});
}
}go deeper
Recall the three parts: final data plus child, static of and maybeOf that call dependOnInheritedWidgetOfExactType, and updateShouldNotify comparing the old widget's data.
Explain that the lookup registers the element as a dependent, that only dependents rebuild when updateShouldNotify returns true, and that a StatefulWidget above supplies new instances.
Show you keep the scope's child instance stable so only dependents rebuild, and that you catch in-place mutation of scoped data during review.
Weigh a hand-written inherited scope against a state-management package for app-wide settings: fewer dependencies and full control versus lifecycle and testing conveniences.
## The problem: data needed far from where it lives In a hiking-trail app, the user picks a **unit system** once in settings: metric or imperial. That choice matters to a trail card's distance label, an elevation chart's axis, a pace field on the recording screen and a weather tile, all several levels below the screen that owns the setting. Without a sharing mechanism you pass `units` through every intermediate constructor. That is **prop drilling**, and it has three costs: - every widget on the path gains a parameter it never reads; - adding one more consumer means editing every ancestor on its path; - when the value changes, the whole path rebuilds, because each constructor argument changed. `InheritedWidget` is Flutter's built-in answer. It is the primitive under `Theme`, `MediaQuery`, `Directionality`, `DefaultTextStyle` and many package-level scopes. ## How you declare one An `InheritedWidget` subclass has three parts: 1. **Immutable data** in `final` fields, plus the required `child` passed to the super constructor. 2. **Static accessors** by convention: `maybeOf(context)` returns `null` when no ancestor exists; `of(context)` asserts or throws with a helpful message instead. Both call `context.dependOnInheritedWidgetOfExactType<T>()`. 3. **`updateShouldNotify(oldWidget)`**, which the framework calls when the widget at this position is replaced by a new instance. It receives the previous widget, which is guaranteed to have the same `runtimeType`, and returns whether dependents need to rebuild. ```dart class UnitsScope extends InheritedWidget { const UnitsScope({super.key, required this.units, required super.child}); final UnitSystem units; static UnitSystem of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<UnitsScope>()!.units; @override bool updateShouldNotify(UnitsScope oldWidget) => units != oldWidget.units; } ``` ## What the framework does with it Each element keeps a map from widget `runtimeType` to the nearest **`InheritedElement`** of that type, inherited from its parent. So finding the nearest `UnitsScope` is a constant-time map lookup, not a walk up the tree. Because the map is keyed by the **exact** runtime type, a subclass of `UnitsScope` would not be found by `dependOnInheritedWidgetOfExactType<UnitsScope>()`. The lookup does two things: - it returns the widget, so the caller can read `units`; - it adds the calling element to the `InheritedElement`'s set of **dependents**. When the scope is rebuilt with a new instance and `updateShouldNotify` returns `true`, the `InheritedElement` calls `didChangeDependencies` on every dependent, which marks each one for rebuild in the next build phase. For a `StatefulWidget` dependent, `State.didChangeDependencies` runs before its next `build`. | Widget in the subtree | Rebuilt when units change? | |---|---| | Called `UnitsScope.of(context)` in build | Yes | | Did not call it, and its parent passed the same widget instance | No | | Did not call it, but its parent was itself rebuilt | Yes, through the ordinary parent rebuild | ## One toggle, step by step Suppose the user switches from metric to imperial on the settings screen: 1. The settings `State` above `UnitsScope` calls `setState` with `UnitSystem.imperial`. 2. In the next build phase that `State` rebuilds and returns a **new** `UnitsScope` instance carrying `imperial` and the same `child`. 3. The `InheritedElement` receives the new widget and calls `updateShouldNotify(oldWidget)`; `imperial != metric`, so it returns `true`. 4. Every registered dependent, such as each `TrailDistance` label, gets `didChangeDependencies` and is marked dirty. 5. The child subtree is compared as usual; because the `child` instance is unchanged, the intermediate list, cards and padding are skipped. 6. The dirty dependents rebuild in the same build phase, read `UnitsScope.of(context)` again and show miles. Nothing between the scope and the labels had to know that units exist, which is the whole point. ## Where the changing value comes from Widgets are immutable, so `UnitsScope` cannot change its own `units`. The usual shape is a `StatefulWidget` above it that holds the current value in its `State`, calls `setState` when the user toggles it, and returns `UnitsScope(units: _units, child: widget.child)` from `build`. Because `widget.child` is the same instance on each rebuild, the subtree between the scope and its dependents is skipped; only dependents rebuild. ## Common mistakes to avoid - Calling the accessor from a `BuildContext` that sits **above** the scope, for example in the same `build` method that creates it. The lookup only sees ancestors of that context, so it does not find a scope created below it. - Returning `true` unconditionally from `updateShouldNotify`, which rebuilds every dependent on every rebuild of the scope even when nothing changed. - Storing a **mutable** object and changing a field in place: no new widget instance is created, so `updateShouldNotify` is never called and nothing rebuilds. ## Where it sits among neighbours Packages such as Provider wrap this primitive with lifecycle management and are their own topic. Holding changing values in a `ChangeNotifier` or `ValueNotifier` is also its own topic. What stays here is the primitive itself: immutable data, a registering lookup and a notify rule.
- Why is the lookup in an InheritedWidget's of method cheap even in a deep tree?Every element carries a map from widget runtime type to the nearest `InheritedElement` of that type, taken from its parent when it mounts. `dependOnInheritedWidgetOfExactType<T>()` is a map lookup, not an ancestor walk, so depth does not matter. The map is keyed by exact type, which is also why a subclass is not found by its parent type.
- If UnitsScope holds a value that changes, which widget owns the changing state?Not `UnitsScope`: widgets are immutable. A `StatefulWidget` above it stores the current unit system in its `State`, calls `setState` on a toggle, and rebuilds `UnitsScope` with the new value and the same `child` instance. That new instance triggers `updateShouldNotify`, and only registered dependents rebuild.
A building's intercom list: residents who ask to be told about the water shutdown are put on the list, and when the notice changes only they get a call, not every flat in the building.
saying these in an interview costs you the question
- InheritedWidget rebuilds its entire subtree whenever its data changes.
- You change the value by mutating a field on the InheritedWidget.
- Finding the nearest InheritedWidget walks up the tree on every call.
- updateShouldNotify should just return true to be safe.
- A subclass instance is found when you look up its parent type.