Understanding InheritedWidget vs InheritedModel in Flutter
InheritedWidget and InheritedModel are both mechanisms in Flutter to pass information down to the child widgets from the parent widget. However, they both serve different purposes and offer varying levels of control over widget rebuilds.
InheritedWidget
InheritedWidget is one of the most used mechanisms to pass information down to child widgets. Whenever the information changes, all the dependent child widgets are rebuilt. InheritedWidget uses a simple `updateShouldNotify` mechanism to detect changes in the information before rebuilding the child widgets.
// Simple InheritedWidget
class UserDataWidget extends InheritedWidget {
final String name;
final int age;
final Function(String) updateName;
final Function(int) updateAge;
const UserDataWidget({
super.key,
required this.name,
required this.age,
required this.updateName,
required this.updateAge,
required super.child,
});
@override
bool updateShouldNotify(UserDataWidget oldWidget) {
return name != oldWidget.name || age != oldWidget.age;
}
static UserDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserDataWidget>()!;
}
}
// Usage Example
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String name = 'John';
int age = 25;
void updateName(String newName) {
setState(() => name = newName);
}
void updateAge(int newAge) {
setState(() => age = newAge);
}
@override
Widget build(BuildContext context) {
return UserDataWidget(
name: name,
age: age,
updateName: updateName,
updateAge: updateAge,
child: MaterialApp(
home: UserProfileScreen(),
),
);
}
}
class UserProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// For InheritedWidget
final userData = UserDataWidget.of(context);
// For InheritedModel (with specific aspect)
final userModel = UserDataModel.of(context, aspect: UserAspect.name);
return Scaffold(
body: Column(
children: [
// Using InheritedWidget
Text('Name: ${userData.name}'),
ElevatedButton(
onPressed: () => userData.updateName('Jane'),
child: Text('Update Name'),
),
// Using InheritedModel
Text('Name: ${userModel?.name}'),
ElevatedButton(
onPressed: () => userModel?.updateName('Jane'),
child: Text('Update Name'),
),
],
),
);
}
}
In the above example, UserProfileScreen
will be rebuilt whenever there is any change in either name
or age
.
InheritedModel
InheritedModel extends InheritedWidget but provides more granular control. InheritedModel allows a widget to subscribe to a specific aspect of data instead of an entire class eg UserData
. This provides selective rebuilding of widgets, based on which data changed.
// InheritedModel with specific aspects
enum UserAspect { name, age }
class UserDataModel extends InheritedModel<UserAspect> {
final String name;
final int age;
UserDataModel({
required this.name,
required this.age,
required Widget child,
}) : super(child: child);
@override
bool updateShouldNotify(UserDataModel oldWidget) {
return name != oldWidget.name || age != oldWidget.age;
}
@override
bool updateShouldNotifyDependent(
UserDataModel oldWidget,
Set<UserAspect> dependencies
) {
return (dependencies.contains(UserAspect.name) && name != oldWidget.name) ||
(dependencies.contains(UserAspect.age) && age != oldWidget.age);
}
static UserDataModel? of(BuildContext context, {UserAspect? aspect}) {
return InheritedModel.inheritFrom<UserDataModel>(context, aspect: aspect);
}
}
// Root widget setup
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return UserDataModel(
name: "John",
age: 30,
child: MaterialApp(
home: HomePage(),
),
);
}
}
// Using InheritedModel with specific aspects
class NameDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Only rebuilds when name changes
final user = UserDataModel.of(context, aspect: UserAspect.name);
return Text(user?.name ?? '');
}
}
class AgeDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Only rebuilds when age changes
final user = UserDataModel.of(context, aspect: UserAspect.age);
return Text('${user?.age ?? 0}');
}
}
In the above example, NameDisplay
and AgeDisplay
widgets will only be rebuilt when name
or age
changes respectively. Since each of these child widgets relies on specific data, they are only rebuilt when their data changes.
Key differences between InheritedWidget and InheritedModel
1. Rebuild Control
InheritedWidget: Rebuilds all dependant widgets whenever data changes
InheritedModel: Allows selective rebuilding of widgets based on specific aspects of data
2. Dependency Specification
InheritedWidget: Widgets depend on the entire data structure
InheritedModel: Widgets can pick the data that they want to depend on
3. Performance Optimisation
InheritedWidget: Less granular control over rebuilds
InheritedModel: Reduces unnecessary builds since widgets can selectively pick data that they depend on
As you have read in the article so far, both InheritedWidget and InheritedModel can used to pass on the dependencies to the child widgets in a widget tree. Whenever you require simple data sharing or the child widgets should be rebuilt whenever the data changes, use InheritedWidgets. However, if you require fine-grained control over widget rebuilds and performance optimization is crucial, use InheritedModel.