Among the various state management solutions, one that often goes underappreciated is the Inherited Widget. It’s a robust, efficient, and flexible technique, largely built into the Flutter framework.
InheritedWidget in Flutter is a special widget designed to propagate information down the widget tree efficiently.
Besides provider and flutter riverport, getting a deep understanding of what an inherited widget is and how to use the inherited widget in Flutter is very crucial at the Developer’s end to create complex UIs.
Let’s get started with it.
What is an Inherited Widget in Flutter?
Inherited Widgets are a vital component of Flutter’s Widget system, providing a powerful and efficient way of passing data down the tree. The fundamental idea behind InheritedWidget is to provide a way to access data from a parent widget to one or more descendant widgets without passing the state down through constructor parameters.
It leverage dart’s object-oriented capabilities and Flutter’s reactive UI updates to create a flexible, intuitive, and performant state management system.
How to Use Inherited Widget Flutter Example
Inherited Widgets create a data layer their descendants can access via their context. The data isn’t stored in the Inherited Widget itself but in a separate model class that the Inherited Widget takes as a parameter.
- First, we’ll create our InheritedWidget: ThemeColor is an InheritedWidget with a color property in this snippet.
class ThemeColor extends InheritedWidget { final Color color; const ThemeColor({Key? key, required this.color, required Widget child}) : super(child: child, key: key); @override bool updateShouldNotify(ThemeColor oldWidget) { return color != oldWidget.color; } static ThemeColor? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<ThemeColor>(); } }
- Next, we’ll create the widget: that uses this ThemeColor: In this Box widget, we are accessing the color from our ThemeColor InheritedWidget and applying it to the Container.
class Box extends StatelessWidget { const Box({super.key}); @override Widget build(BuildContext context) { final color = ThemeColor.of(context)?.color ?? Colors.black; return Container( width: 100, height: 100, color: color, ); } }
- Now, we use these widgets in our main application: In the below snippet, we’re wrapping our Scaffold with the ThemeColor InheritedWidget and setting the color to Colors.red. Then, within the Box widget, we access the ThemeColor InheritedWidget and apply the color to our box.
void main() => runApp(MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: ThemeColor( color: Colors.red, child: Scaffold( appBar: AppBar(), body: const Center(child: Box()), ), ), ); } }
This is a simple example of how to use InheritedWidget to pass down data (in this case, a color) to descendant widgets in Flutter.
Inherited Widget Vs. Provider
Features | Inherited Widget | Flutter |
Complexity | Complex | Simple |
State Managment | Propagates Data | Handles State changes and listener Notifications |
Multi Provider Capability | No Built-in. Have to Set it Manually | Has ‘multi provider’ widget |
Lifecycle Management | Manual | Built-in |
Performance | Good | Optimized |
What is the Difference Between InheritedWidget and Inherited Model?
InheritedWidget and InheritedModel are Flutter classes that propagate information down the widget tree. However, they have a key difference in determining when to notify dependents of changes.
When an InheritedWidget changes, based on its updateShouldNotify method, it will notify all dependent widgets in the tree, and those widgets will rebuild.
Whereas the InheritedModel will only notify those dependent widgets related to the changed aspects, leading to potentially fewer widget rebuilds.
Flutter InheritedWidget with ChangeNotifier
ChangeNotifier is a class provided by Flutter that can be used with InheritedWidget to manage the state. It provides a way to listen for changes to a model and allows all listeners (typically widgets) to update their state accordingly.
An InheritedWidget provides a way to share data across the widget tree, and a ChangeNotifier provides a way to listen for changes to that data. They can help efficiently manage and respond to changes in your application’s state when used together.
Example: How to Use ChangeNotifier with InheritedWidget
- Let’s define a simple ChangeNotifier: In the Counter class mentioned below, notifyListeners() is called whenever the count changes. This will trigger any listeners of this ChangeNotifier to rebuild.
class Counter with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } }
- Now, let’s create our InheritedNotifier: Here, InheritedNotifier is a specialized kind of InheritedWidget that works well with ChangeNotifier. It automatically calls updateShouldNotify when the ChangeNotifier calls notifyListeners, causing the widgets listening to this InheritedNotifier to rebuild.
class CounterInheritedNotifier extends InheritedNotifier<Counter> { const CounterInheritedNotifier({ super.key, required Widget child, required Counter notifier, }) : super(child: child, notifier: notifier); static CounterInheritedNotifier? of(BuildContext context) { return context .dependOnInheritedWidgetOfExactType<CounterInheritedNotifier>(); } }
- Finally, you could use your CounterInheritedNotifier in your widget tree like this:
Counter _counter = Counter(); CounterInheritedNotifier( notifier: _counter, child: YourWidgetTree(), );
- Then in any descendant widget, you could access the counter and increment it like this:
final counterInheritedNotifier = CounterInheritedNotifier.of(context); final counter = counterInheritedNotifier?.notifier; final count = counter?.count ?? 0; ElevatedButton( onPressed: counter?.increment, child: const Text('Increment'), )
In this setup, when you call the counter.increment, it updates the count and then calls notifyListeners, triggering updateShouldNotify in the InheritedNotifier, causing all widgets to listen to the InheritedNotifier to rebuild.
Here is a visual representation of the above-mentioned code:
3 Key Features of Flutter Inherited Widget
You might be aware of the common technicalities; the following are some ways where you could increase the efficiency of the inherited widget
-
Automatic Rebuilds
If the inherited data changes, InheritedWidget will immediately rebuild dependent widgets. Because of this, updating the UI in response to changes in the shared state is simple and doesn’t need manually handle state dependencies and trigger updates. The UI will accurately reflect the most recent state of the application since the widgets that rely on the inherited data will be effectively regenerated.
-
Ancestor Access
It is another hidden gem that can aid with more advanced use cases. Widgets can access their ancestor Inherited Widgets to retrieve data. This feature can be handy when dealing with nested data models or scoped themes.
-
Querying Multiple Inherited Widgets
While Inherited Widgets are great for passing data down the tree, they can also query data up the tree. This unique feature is useful when accessing multiple data models within the same widget.
MyDataModel1 model1 = MyInheritedWidget1.of(context).myData; MyDataModel2 model2 = MyInheritedWidget2.of(context).myData;
Conclusion
Understanding InheritedWidget is vital, as it is a cornerstone to mastering Flutter’s state management systems. It offers high flexibility, simplicity, and performance, making it a valuable tool for developers. If you are still unclear and require expert assistance in Flutter development, don’t hesitate to contact us—we’re here to help!