Flutter Intermediate Lesson 44 of 135 3 min read
InheritedModel in Flutter: Rebuild Only Widgets That Use Changed Data
Learn InheritedModel with a simple example: share one model and let each widget depend on only the part it uses, using aspects and updateShouldNotifyDependent.
On this page
With a normal InheritedWidget, every widget that reads it rebuilds when any of its data changes. InheritedModel is smarter: each widget says which aspect (part) of the data it cares about, and only rebuilds when that part changes.
MediaQuery works this way. MediaQuery.sizeOf(context) only rebuilds your widget when the size changes, not when, say, the keyboard padding changes.
Example: a user profile with two parts
The name and the score change independently. The widget showing the name should not rebuild when the score changes.
import 'package:flutter/material.dart';
enum ProfileAspect { name, score }
class ProfileModel extends InheritedModel<ProfileAspect> {
const ProfileModel({
super.key,
required this.name,
required this.score,
required super.child,
});
final String name;
final int score;
static ProfileModel of(BuildContext context, ProfileAspect aspect) {
return InheritedModel.inheritFrom<ProfileModel>(context, aspect: aspect)!;
}
// Has anything changed at all?
@override
bool updateShouldNotify(ProfileModel old) =>
name != old.name || score != old.score;
// Has the part this widget depends on changed?
@override
bool updateShouldNotifyDependent(
ProfileModel old, Set<ProfileAspect> dependencies) {
return (dependencies.contains(ProfileAspect.name) && name != old.name) ||
(dependencies.contains(ProfileAspect.score) && score != old.score);
}
}
void main() => runApp(const MaterialApp(home: ProfilePage()));
class ProfilePage extends StatefulWidget {
const ProfilePage({super.key});
@override
State<ProfilePage> createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
String _name = 'Asha';
int _score = 0;
@override
Widget build(BuildContext context) {
return ProfileModel(
name: _name,
score: _score,
child: Scaffold(
appBar: AppBar(title: const Text('InheritedModel')),
body: const Padding(
padding: EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [NameLabel(), SizedBox(height: 12), ScoreLabel()],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton.small(
heroTag: 'name',
onPressed: () => setState(() => _name = _name == 'Asha' ? 'Bikram' : 'Asha'),
child: const Icon(Icons.person),
),
const SizedBox(height: 8),
FloatingActionButton(
heroTag: 'score',
onPressed: () => setState(() => _score++),
child: const Icon(Icons.add),
),
],
),
),
);
}
}
class NameLabel extends StatelessWidget {
const NameLabel({super.key});
@override
Widget build(BuildContext context) {
final name = ProfileModel.of(context, ProfileAspect.name).name;
debugPrint('NameLabel rebuilt');
return Text('Name: $name', style: const TextStyle(fontSize: 24));
}
}
class ScoreLabel extends StatelessWidget {
const ScoreLabel({super.key});
@override
Widget build(BuildContext context) {
final score = ProfileModel.of(context, ProfileAspect.score).score;
debugPrint('ScoreLabel rebuilt');
return Text('Score: $score', style: const TextStyle(fontSize: 24));
}
}
Open the console and press + a few times: only ScoreLabel rebuilt is printed. Press the person button: only NameLabel rebuilt. That is the whole point of InheritedModel.
The two methods
| Method | Question it answers |
|---|---|
updateShouldNotify(old) | Did anything change? If false, nobody rebuilds. |
updateShouldNotifyDependent(old, dependencies) | Did the parts this widget asked for change? |
dependencies is the set of aspects the widget passed to inheritFrom.
When to use it
- A big shared object (user, settings, theme data) read by many small widgets.
- Performance problems where unrelated widgets rebuild.
For most apps, splitting state into several small models (or using select in Provider or Riverpod) gets the same result more simply. InheritedModel is mainly worth knowing because Flutter itself uses it.
Try it yourself
Add a third aspect, avatarColor, with a button that cycles through three colours, and a CircleAvatar that only rebuilds when the colour changes.