I can give you a simple example: how can a const
child widget of the InheritedWidget
ever be rebuilt if it is never notified?
Run this sample code to see the behavior. Change the return value of updateShouldNotify
from false
to true
and then tap on the button.
/// This is the [StatefulWidget] that will rebuild the [InheritedWidget].
class InheritedParent extends StatefulWidget {
const InheritedParent({super.key});
@override
State<InheritedParent> createState() => _InheritedParentState();
}
class _InheritedParentState extends State<InheritedParent> {
@override
Widget build(BuildContext context) {
return Inherited(
/// Pass the random color to the [InheritedWidget].
color: _getRandomColor(),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// The inherited child is constant.
const InheritedChild(),
FloatingActionButton(
/// We call setState, so the entire subtree should be rebuilt...
/// but what about 'const InheritedChild()'?
onPressed: () => setState(() {}),
child: const Icon(Icons.add),
),
],
),
),
);
}
/// This method will return a random color.
Color _getRandomColor() {
return Colors.primaries[math.Random().nextInt(Colors.primaries.length)];
}
}
/// The widget that provides a [Color] to its descendants through the context.
class Inherited extends InheritedWidget {
const Inherited({
super.key,
required this.color,
required super.child,
});
final Color color;
static Inherited? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<Inherited>();
}
@override
bool updateShouldNotify(Inherited oldWidget) {
/// Change this to see the behavior.
return false;
}
}
class InheritedChild extends StatefulWidget {
const InheritedChild({super.key});
@override
State<InheritedChild> createState() => _InheritedChildState();
}
class _InheritedChildState extends State<InheritedChild> {
@override
Widget build(BuildContext context) {
return Container(
width: 128.0,
height: 128.0,
/// Access the color specified in 'Inherited'.
color: Inherited.of(context)?.color,
);
}
}
The InheritedWidget
allows you to filter rebuilds (calls to setState
so that descendants are only rebuilt when any data that they depend on (from the parent InheritedWidget) changes.
While the convenience idiom is to provide a static of(context)
method for your inherited widgets, remember that they can be used without one; to make it explicit:
class _InheritedChildState extends State<InheritedChild> {
@override
Widget build(BuildContext context) {
return Container(
width: 128.0,
height: 128.0,
/// This widget DEPENDS on the [Inherited] to rebuild.
color: context.dependOnInheritedWidgetOfExactType<Inherited>()?.color,
);
}
}
From the documentation: "Returns the nearest widget of the given type T and creates a dependency on it".
When a dependency is created, the child widget is registered for changes. When does it get notified of the changes?
When updateShouldNotify
return true
.
Therefore, the correct implementation of Inherited.updateShouldNotify
is:
@override
bool updateShouldNotify(Inherited oldWidget) {
/// The data that the child widget depends on in order to properly
/// build itself.
return oldWidget.color != color;
}