Using InheritedWidget
1. Understanding InheritedWidget Concept
InheritedWidget is a Flutter widget that allows data to be efficiently shared between multiple widgets in the widget tree without requiring each widget to rebuild when the data changes.
| Property | Description | Usage |
|---|---|---|
| updateShouldNotify | Override this method to control when child widgets should rebuild. | |
| of | Static method to access the nearest InheritedWidget of a given type. |
MyInheritedWidget.of(context) |
Example: Basic InheritedWidget Usage
class MyInheritedWidget extends InheritedWidget {
final String data;
const MyInheritedWidget({
required this.data,
required Widget child,
}) : super(child: child);
static MyInheritedWidget of(BuildContext context) {
return context.findAncestorWidgetOfExactType<MyInheritedWidget>()!;
}
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.data != data;
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = MyInheritedWidget.of(context).data;
return Text(data);
}
}
Note:
InheritedWidget is particularly useful for sharing theme data, localization, or other frequently accessed values across a widget tree.
2. Creating InheritedWidget Subclasses
| Property | Type | Description |
|---|---|---|
| constructor | InheritedWidget |
Creates a new InheritedWidget instance. |
| updateShouldNotify | bool Function(InheritedWidget oldWidget) |
Override to control when dependent widgets should rebuild. |
Example: Basic InheritedWidget Implementation
class ThemeData {
final Color primaryColor;
ThemeData({required this.primaryColor});
}
class ThemeInheritedWidget extends InheritedWidget {
final ThemeData themeData;
ThemeInheritedWidget({
required this.themeData,
required Widget child,
}) : super(child: child);
static ThemeData of(BuildContext context) {
final ThemeInheritedWidget? result =
context.getElementForInheritedWidgetOfExactType<ThemeInheritedWidget>();
return result?.themeData ?? ThemeData(primaryColor: Colors.blue);
}
@override
bool updateShouldNotify(ThemeInheritedWidget oldWidget) {
return themeData != oldWidget.themeData;
}
}
Note:
InheritedWidget is a powerful way to efficiently propagate data down the widget tree without requiring each descendant to explicitly receive the data.
3. Implementing updateShouldNotify() Method
| Method | Description | Return Type | Notes |
|---|---|---|---|
| updateShouldNotify() | Override to control when widgets should rebuild | bool |
Must return true to trigger rebuilds when data changes |
Example: Basic Implementation
class MyInheritedWidget extends InheritedWidget {
final String data;
MyInheritedWidget({
required Widget child,
required this.data,
}) : super(child: child);
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return data != oldWidget.data;
}
}
Example: Advanced Implementation with Multiple Values
class UserInheritedWidget extends InheritedWidget {
final User user;
final Theme theme;
UserInheritedWidget({
required Widget child,
required this.user,
required this.theme,
}) : super(child: child);
@override
bool updateShouldNotify(UserInheritedWidget oldWidget) {
return user.id != oldWidget.user.id ||
theme.name != oldWidget.theme.name;
}
}
Note: The
updateShouldNotify method is called whenever the parent widget rebuilds. It's crucial to implement it correctly to avoid unnecessary rebuilds.
4. Accessing InheritedWidget
| Method | Description | Usage |
|---|---|---|
| InheritedWidget.of(context) | Retrieves the nearest ancestor InheritedWidget of the specified type. | final widget = MyInheritedWidget.of(context); |
| InheritedWidget.of(context, type) | Retrieves the nearest ancestor of a specific type. | final widget = InheritedWidget.of<MyInheritedWidget>(context); |
Example: Accessing InheritedWidget in a Widget
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final inheritedData = MyInheritedWidget.of(context);
return Text(inheritedData.data);
}
}
Note: Ensure that the
context is within a widget tree that has an ancestor of the desired InheritedWidget.
5. Using InheritedWidget.of(context) Method
| Method | Description | Return Type | Notes |
|---|---|---|---|
| InheritedWidget.of(context) | Retrieves the nearest ancestor InheritedWidget of the specified type from the widget tree. | InheritedWidget or null |
Used to access data from parent widgets that use InheritedWidget. |
Example: Accessing InheritedWidget Data
class MyInheritedWidget extends InheritedWidget {
final String data;
MyInheritedWidget({required this.data, required Widget child}) : super(child: child);
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.data != data;
}
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>()!;
}
}
class ConsumerWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final widgetData = MyInheritedWidget.of(context).data;
return Text(widgetData);
}
}
Note: The
of method is a static helper that simplifies accessing the widget instance. It uses context.dependOnInheritedWidgetOfExactType internally.
6. Understanding BuildContext and Dependencies
| Aspect | Description | Usage |
|---|---|---|
| BuildContext | Represents the location of a widget in the widget tree. | Used to access inherited widgets, themes, and other context-specific data. |
| Dependencies | Tracks which widgets depend on specific data or services. | Used for efficient re-rendering when dependencies change. |
Example: Accessing BuildContext
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Accessing theme from BuildContext
final theme = Theme.of(context);
return Text(
'Theme color: ${theme.primaryColor}',
style: theme.textTheme.headlineMedium,
);
}
}
Example: Using InheritedWidget with BuildContext
class MyAppTheme extends InheritedWidget {
final Color primaryColor;
MyAppTheme({
required this.primaryColor,
required Widget child,
}) : super(child: child);
static MyAppTheme of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyAppTheme>()!;
}
@override
bool updateShouldNotify(MyAppTheme oldWidget) {
return primaryColor != oldWidget.primaryColor;
}
}
class ThemeConsumer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = MyAppTheme.of(context);
return Container(
color: theme.primaryColor,
child: Text('Using inherited theme'),
);
}
}
Note: BuildContext is essential for accessing inherited widgets and managing widget dependencies efficiently.
Key Points
- BuildContext provides access to the widget tree and inherited widgets.
- Dependencies help Flutter optimize rebuilds by tracking widget dependencies.
- InheritedWidget is a powerful way to share data across the widget tree efficiently.
7. Propagating Data Down Widget Tree
In Flutter, InheritedWidget is a powerful mechanism for efficiently propagating data down the widget tree. It allows data to be shared across multiple widgets without requiring each widget to explicitly receive the data as a parameter.
| Property | Description | Usage |
|---|---|---|
| updateShouldNotify | Override this method to control when widgets should rebuild when the data changes. | @override bool updateShouldNotify(MyInheritedWidget oldWidget) { return oldWidget.data != data; } |
Example: Basic InheritedWidget Implementation
class MyInheritedWidget extends InheritedWidget {
final String data;
const MyInheritedWidget({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
static MyInheritedWidget of(BuildContext context) {
final widget = context.getElementForInheritedWidgetOfExactType<MyInheritedWidget>();
return widget as MyInheritedWidget;
}
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return data != oldWidget.data;
}
}
Example: Using InheritedWidget in a Widget Tree
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MyInheritedWidget(
data: 'Shared Data',
child: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = MyInheritedWidget.of(context).data;
return Text(data);
}
}
Note:
InheritedWidget is particularly useful for sharing data like theme information, user preferences, or app-wide state without requiring widgets to pass this data explicitly down the tree.
8. Using InheritedNotifier Widget
The InheritedNotifier widget in Flutter is a specialized version of InheritedWidget designed to efficiently notify listeners when a ChangeNotifier or ValueNotifier changes. It's particularly useful for managing state that needs to be shared across multiple parts of the widget tree without requiring a full rebuild.
| Property | Description | Usage |
|---|---|---|
| notifier | The ChangeNotifier or ValueNotifier instance to observe. |
Required. Must be a valid notifier object. |
| child | The child widget tree that will have access to the notifier's state. | Optional. Defaults to null. |
Example: Using InheritedNotifier to Share State
import 'package:flutter/material.dart';
class CounterNotifier extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class CounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return InheritedNotifier(
notifier: CounterNotifier(),
child: Column(
children: [
Text("Count: ${CounterNotifier().count}"),
ElevatedButton(
onPressed: () {
CounterNotifier().increment();
},
child: Text("Increment"),
),
],
),
);
}
}
Note:
InheritedNotifier is optimized for performance and ensures that only widgets listening to the notifier are rebuilt when state changes.
9. Implementing Theme and Locale with InheritedWidget
| Property | Description | Usage |
|---|---|---|
| InheritedWidget | Base class for widgets that efficiently propagate data down the widget tree. | Extend to create custom inherited widgets for theme or locale data. |
| updateShouldNotify | Override to control when child widgets should rebuild. | Return true if changes to the data require a rebuild. |
Example: Custom Theme InheritedWidget
class CustomTheme extends InheritedWidget {
final Color primaryColor;
const CustomTheme({
required this.primaryColor,
required Widget child,
}) : super(child: child);
static CustomTheme of(BuildContext context) {
return context.findAncestorWidgetOfExactType<CustomTheme>()!;
}
@override
bool updateShouldNotify(CustomTheme old) => primaryColor != old.primaryColor;
}
Example: Using Theme in Widget Tree
class ThemeConsumer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = CustomTheme.of(context);
return Container(
color: theme.primaryColor,
child: Text('Themed Content'),
);
}
}
Example: Locale InheritedWidget
class LocaleProvider extends InheritedWidget {
final Locale locale;
const LocaleProvider({
required this.locale,
required Widget child,
}) : super(child: child);
static LocaleProvider of(BuildContext context) {
return context.findAncestorWidgetOfExactType<LocaleProvider>()!;
}
@override
bool updateShouldNotify(LocaleProvider old) => locale != old.locale;
}
Note:
InheritedWidget is ideal for propagating immutable data like theme or locale efficiently down the widget tree.
10. Understanding Performance Implications
| Aspect | Description | Impact |
|---|---|---|
| Widget Tree Rebuilds | InheritedWidget changes trigger rebuilds of all descendant widgets that depend on it. | Can cause unnecessary UI updates if not managed carefully. |
| Notification Propagation | Changes to InheritedWidget values propagate down the tree, even if not directly used. | May lead to performance degradation in large widget trees. |
| Memory Usage | Each InheritedWidget instance holds a copy of its data, potentially increasing memory usage. | High if many widgets depend on the same InheritedWidget with large data sets. |
Note: Use
const constructors where possible to reduce rebuilds and optimize performance.Example: Efficient InheritedWidget Usage
class MyInheritedWidget extends InheritedWidget {
final String data;
const MyInheritedWidget({
Key? key,
required Widget child,
required this.data,
}) : super(key: key, child: child);
static MyInheritedWidget of(BuildContext context) {
final MyInheritedWidget? result = context
.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
return result!;
}
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return data != oldWidget.data;
}
}
class ConsumerWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = MyInheritedWidget.of(context).data;
return Text(data);
}
}