Education
Software Development Executive - II
Last updated on Dec 4, 2024
Last updated on Dec 4, 2024
For Flutter enthusiasts, InheritedWidget serves as a cornerstone for seamless data propagation across the widget hierarchy. It eliminates the need for repetitive data transmission, fostering clarity and precision in your code.
By enabling widgets to access shared information effortlessly, this ingenious class becomes an indispensable tool for building dynamic, responsive applications. Whether you're navigating the intricacies of state handling or refining your app’s architecture, InheritedWidget is your pathway to efficiency.
InheritedWidget serves as a base class for widgets that need to share data with their descendants. It enables the data to be accessed by any widget in the subtree below it without explicitly passing it down every level. This makes managing and accessing shared state more convenient and reduces boilerplate code.
To grasp the concept of InheritedWidget better, it's essential to understand the widget tree and widget hierarchy in Flutter. The widget tree represents the structure of widgets in a Flutter application, with the root widget at the top and the UI components branching out below it.
The parent and child relationships define the widget hierarchy in this tree structure. Parent widgets have child widgets, and changes made in a parent widget can affect its descendant widgets. However, passing data between distant widgets can become tedious and inefficient.
This is where InheritedWidget comes into play. It allows data to be "inherited" by all the widgets below it in the tree, making it easily accessible to any descendent widget without manual passing. By utilizing InheritedWidget, developers can manage state more effectively and improve the performance of their Flutter applications.
Also read: Flutter Widget Cheatsheet
InheritedWidget is a powerful tool for managing state in Flutter applications. Let's explore how to implement and utilize it effectively.
To harness the power of InheritedWidget for state management, we need to create a custom class that extends it. This custom class will define the data we want to share and provide methods for accessing and updating the data.
Here's an example of a custom InheritedWidget class called MyStateWidget:
1class MyStateWidget extends InheritedWidget { 2 const MyStateWidget({ 3 Key? key, 4 required this.data, 5 required Widget child, 6 }) : super(key: key, child: child); 7 8 final int data; 9 10 static MyStateWidget? of(BuildContext context) { 11 return context.dependOnInheritedWidgetOfExactType<MyStateWidget>(); 12 } 13 14 15 bool updateShouldNotify(MyStateWidget oldWidget) { 16 return oldWidget.data != data; 17 } 18} 19
In this example, we define MyStateWidget with an int data property. We also implement the of method, which allows us to access the nearest instance of MyStateWidget from a given BuildContext. The updateShouldNotify method is overridden to determine whether the widget should notify its dependents when the data changes.
Once we have our custom InheritedWidget class, we can incorporate it into the widget tree of our application. By placing it higher up in the tree, we ensure that all descendant widgets can access the shared data.
Here's an example of how we can build the widget tree with Inherited Widgets:
1class MyApp extends StatelessWidget { 2 3 Widget build(BuildContext context) { 4 return MyStateWidget( 5 data: 42, 6 child: MaterialApp( 7 title: 'My App', 8 home: MyHomePage(), 9 ), 10 ); 11 } 12} 13 14class MyHomePage extends StatelessWidget { 15 16 Widget build(BuildContext context) { 17 final myData = MyStateWidget.of(context)?.data ?? 0; 18 19 return Scaffold( 20 appBar: AppBar( 21 title: Text('Home Page'), 22 ), 23 body: Center( 24 child: Text('Shared Data: $myData'), 25 ), 26 ); 27 } 28} 29
In this example, we wrap the MaterialApp widget with our custom InheritedWidget, MyStateWidget. This ensures that any descendant widgets, such as MyHomePage, can access the shared data.
Within MyHomePage, we use the of method to retrieve the shared data from the nearest MyStateWidget ancestor. If the MyStateWidget is not found, we provide a default value of 0. We display the shared data in the body of the scaffold.
Building the widget tree with Inherited Widgets establishes a mechanism for sharing and accessing data throughout our application, facilitating efficient state management and updates.
InheritedWidget is crucial in updating widgets efficiently when changes occur in the shared data. Let's explore how InheritedWidget enables effective state management and handles updates in Flutter applications.
State management is a critical aspect of developing Flutter apps, especially when dealing with complex UIs and data that needs to be shared across multiple widgets. While stateful widgets can handle basic state management, they can become cumbersome and less efficient when managing complex UIs with deep widget hierarchies.
This is where InheritedWidget shines. It provides a mechanism for efficiently sharing state across descendant widgets, eliminating the need for manual data passing and reducing boilerplate code. InheritedWidget acts as a container for shared data, allowing any widget in the subtree below it to access that data and respond to changes.
InheritedWidget ensures that any changes in the shared data propagate down the widget tree, notifying all dependent widgets to update their state.
The updateShouldNotify method in the custom InheritedWidget class determines whether dependent widgets should be notified of changes. By comparing the old and new values of the shared data, we can trigger updates only when necessary.
Here's an example implementation of the updateShouldNotify method:
1class MyStateWidget extends InheritedWidget { 2 // ... 3 4 5 bool updateShouldNotify(MyStateWidget oldWidget) { 6 return oldWidget.data != data; 7 } 8} 9
In this example, the updateShouldNotify method compares the data property of the old and new widget instances. If the values differ, it returns true, indicating that dependents should be notified of the change. Otherwise, it returns false.
This mechanism allows updates to be selectively propagated down the widget tree, preventing unnecessary rebuilds of widgets that don't depend on the changed data.
We control how and when updates are triggered by implementing the updateShouldNotify method within the custom InheritedWidget class. We can define the conditions under which changes in the shared data should result in updates to dependent widgets.
For example, if the shared data is an object and only certain properties of that object should trigger updates, we can compare those specific properties in the updateShouldNotify method. This fine-grained control allows for efficient and precise updates, optimizing the performance of our Flutter applications.
1class MyData { 2 final String name; 3 final int age; 4 5 MyData(this.name, this.age); 6} 7 8class MyStateWidget extends InheritedWidget { 9 final MyData data; 10 11 // ... 12 13 14 bool updateShouldNotify(MyStateWidget oldWidget) { 15 return oldWidget.data.name != data.name || oldWidget.data.age != data.age; 16 } 17} 18
MyData is a custom data class containing properties like name and age in this example. By comparing the individual properties in the updateShouldNotify method, we can determine whether updates should be triggered based on specific changes to the shared data.
This level of control over updates ensures that only widgets that depend on the changed data will be rebuilt, improving the efficiency and performance of our Flutter applications.
To illustrate the practical usage of InheritedWidget, let's explore an example of building a counter app using this powerful state management approach.
Imagine we want to create a simple counter app that allows users to increment and decrement a counter value. Let's see how we can implement this using InheritedWidget.
To build our counter app, we'll need a few widgets and classes:
Let's define the MyCounterWidget, a stateful widget that holds the counter value and manages state changes.
1class MyCounterWidget extends StatefulWidget { 2 const MyCounterWidget({Key? key, required this.child}) : super(key: key); 3 4 final Widget child; 5 6 7 _MyCounterWidgetState createState() => _MyCounterWidgetState(); 8} 9 10class _MyCounterWidgetState extends State<MyCounterWidget> { 11 int _counter = 0; 12 13 void incrementCounter() { 14 setState(() { 15 _counter++; 16 }); 17 } 18 19 void decrementCounter() { 20 setState(() { 21 _counter--; 22 }); 23 } 24 25 26 Widget build(BuildContext context) { 27 return CounterProvider( 28 counter: _counter, 29 increment: incrementCounter, 30 decrement: decrementCounter, 31 child: widget.child, 32 ); 33 } 34} 35
In this implementation, we define the MyCounterWidget as a StatefulWidget. The counter value is stored in the state using the _MyCounterWidgetState class. We provide methods for incrementing and decrementing the counter value, which call setState to trigger updates.
We wrap the child widget passed to MyCounterWidget with the CounterProvider, our custom InheritedWidget class responsible for sharing the counter value and update methods.
Next, let's implement the CounterProvider class, which extends InheritedWidget and holds the counter value and update methods.
1class CounterProvider extends InheritedWidget { 2 const CounterProvider({ 3 Key? key, 4 required this.counter, 5 required this.increment, 6 required this.decrement, 7 required Widget child, 8 }) : super(key: key, child: child); 9 10 final int counter; 11 final VoidCallback increment; 12 final VoidCallback decrement; 13 14 static CounterProvider? of(BuildContext context) { 15 return context.dependOnInheritedWidgetOfExactType<CounterProvider>(); 16 } 17 18 19 bool updateShouldNotify(CounterProvider oldWidget) { 20 return oldWidget.counter != counter; 21 } 22} 23
In the CounterProvider class, we define the counter value and the increment and decrement methods. We also implement the of method to retrieve the nearest CounterProvider widget from the given BuildContext.
The updateShouldNotify method compares the old and new counter values, determining when updates should be triggered. In this case, updates are triggered whenever the counter value changes.
Let's create the CounterScreen and CounterButton widgets to display and manipulate the counter value.
1class CounterScreen extends StatelessWidget { 2 3 Widget build(BuildContext context) { 4 final counterProvider = CounterProvider.of(context); 5 6 return Scaffold( 7 appBar: AppBar( 8 title: const Text('Counter App'), 9 ), 10 body: Center( 11 child: Text( 12 'Counter: ${counterProvider?.counter ?? 0}', 13 style: const TextStyle(fontSize: 24), 14 ), 15 ), 16 ); 17 } 18} 19 20class CounterButton extends StatelessWidget { 21 22 Widget build(BuildContext context) { 23 final counterProvider = CounterProvider.of(context); 24 25 return Row( 26 mainAxisAlignment: MainAxisAlignment.center, 27 children: [ 28 IconButton( 29 icon: const Icon(Icons.remove), 30 onPressed: counterProvider?.decrement, 31 ), 32 IconButton( 33 icon: const Icon(Icons.add), 34 onPressed: counterProvider?.increment, 35 ), 36 ], 37 ); 38 } 39} 40
In the CounterScreen widget, we use CounterProvider.of to access the counter value from the nearest CounterProvider ancestor. We display the counter value using the Text widget.
InheritedWidget isn’t just a utility; it’s a transformative tool for Flutter developers. By effortlessly weaving shared data through the widget hierarchy, it simplifies communication while keeping your code clean and concise.
Its intuitive approach removes the clutter of repetitive data passing, empowering you to focus on creating engaging, high-performance applications. Whether you're fine-tuning state management or architecting a seamless user experience, InheritedWidget is your gateway to crafting apps with precision and flair.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.