Flutter InheritedWidget 2025 (Mastering State Management)
Let’s learn how to use Flutter’s InheritedWidget to manage and share state across your Flutter app. it doesnot matter whether you are handling simple data like a counter or more complex settings such as user preferences and themes, InheritedWidget provides a clean and efficient way to pass data down the widget tree.
We’ll practically learn how it works, how to implement it, and how it can help keep your app’s UI consistent while reducing unnecessary code. We’ll go through multiple Flutter code examples with explanation so you can properly understand this Flutter widget.
What is a Flutter InheritedWidget and why use it?
An InheritedWidget in Flutter is a specialised widget designed to efficiently pass data down the widget tree. It allows descendant widgets to access shared data without needing to pass it through each widget’s constructor. This is particularly useful when multiple widgets need access to the same data, like theme settings, user preferences, or app-wide states.
Main Advantage
The main advantage of using an InheritedWidget is that it helps you avoid “prop drilling” which is the need to manually pass data through several layers of widgets. It automatically rebuilds widgets that depend on the data whenever it changes, keeping the app consistent without adding extra code. This makes it a great tool for managing global states like authentication status, theme data, or language preferences.
Syntax of Inherited Widget
1. Basic Structure
To create an InheritedWidget, you extend the InheritedWidget class. The widget requires a constructor that takes the shared data (like a counter value) and a child widget, which represents the subtree that can access this data. Here’s the basic structure:
class MyInheritedWidget extends InheritedWidget { final int counter; const MyInheritedWidget({ Key? key, required this.counter, required Widget child, }) : super(key: key, child: child); @override bool updateShouldNotify(MyInheritedWidget oldWidget) { return oldWidget.counter != counter; } }
Explanation
- counter: This is the data you want to share with the widgets.
- child: This is the widget tree that will have access to the shared data.
2. Overriding updateShouldNotify
The updateShouldNotify method determines if the data has changed and whether dependent widgets should be notified to rebuild. It gets called whenever the InheritedWidget is updated. This method compares the current and previous data values and returns true if the data has changed (indicating that widgets dependent on it should be rebuilt).
@override bool updateShouldNotify(MyInheritedWidget oldWidget) { return oldWidget.counter != counter; }
In this case, if the counter value changes, updateShouldNotify returns true, triggering a rebuild of any widget dependent on this InheritedWidget.
3. Accessing Data with of(context)
To access the data stored in an InheritedWidget, use the static of(context) method. This method retrieves the nearest InheritedWidget of the specified type from the widget tree. You can use this in any descendant widget that needs access to the shared data.
class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { final inherited = MyInheritedWidget.of(context); final counter = inherited?.counter ?? 0; return Scaffold( appBar: AppBar(title: Text('InheritedWidget Example')), body: Center(child: Text('Counter: $counter')), ); } }
Explanation
- of(context): This is how descendant widgets access the shared data (like the counter value).
- If the data in the InheritedWidget changes, the MyHomePage widget will automatically rebuild with the updated data.
- In the Scaffold, we have Flutter appbar and in body, we are showing a text using Flutter text widget.
You can use this structure to make sure that your data is shared efficiently across widgets while maintaining high performance in your Flutter app.
Practical Code Examples of Using InheritedWidget Flutter
Example 1: A Counter Widget
In this example, we’ll use InheritedWidget to manage a simple counter that can be accessed and updated across different widgets in the widget tree. Let’s implement and understand it practically.
1.1 Creating the InheritedWidget
First, we need to create the InheritedWidget that holds the shared data (in this case, the counter value). We define a class that extends InheritedWidget and provides access to the data via a constructor.
Here’s the code:
class CounterInheritedWidget extends InheritedWidget { final int counter; const CounterInheritedWidget({ Key? key, required this.counter, required Widget child, }) : super(key: key, child: child); static CounterInheritedWidget? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<CounterInheritedWidget>(); } @override bool updateShouldNotify(CounterInheritedWidget oldWidget) { return oldWidget.counter != counter; // Trigger rebuild if counter changes } }
Explanation
- CounterInheritedWidget: This class holds the counter data and extends InheritedWidget. It’s the container for the shared data.
- updateShouldNotify: This method compares the current counter value with the previous one. If the counter changes, it returns true, triggering the rebuild of dependent widgets.
- of(context): This static method is how child widgets access the counter value. It uses context.dependOnInheritedWidgetOfExactType to get the nearest CounterInheritedWidget up the widget tree.
This code makes sure that the counter value is easily accessible by any widget in the tree that depends on it. When the counter changes, only the necessary widgets are rebuilt, which keeps the app efficient.
1.2 Wrapping the Widget Tree
Next, we need to wrap the part of the widget tree that requires access to the counter value with the CounterInheritedWidget. We typically do this at the root level of the app or in a section of the widget tree where the shared data is relevant.
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return CounterInheritedWidget( counter: 0, // Initial counter value child: MaterialApp( home: MyHomePage(), ), ); } }
Explanation
The CounterInheritedWidget is used to wrap the MyHomePage widget, providing it with access to the counter value.
1.3 Accessing the Data in a Descendant Widget
Now that the widget tree is wrapped with the CounterInheritedWidget, we can access the counter value in any descendant widget. We use the of(context) method to get the nearest CounterInheritedWidget and retrieve the counter value.
class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { final counter = CounterInheritedWidget.of(context)?.counter ?? 0; return Scaffold( appBar: AppBar(title: Text('InheritedWidget Example')), body: Center( child: Text('Counter: $counter', style: TextStyle(fontSize: 24)), ), ); } }
Explanation
- CounterInheritedWidget.of(context): This method gets the current CounterInheritedWidget from the widget tree and retrieves the counter.
- The counter value is then displayed in the widget tree.
Summary of this Code Example
- Creating the InheritedWidget: The CounterInheritedWidget holds the counter data and manages when widgets should rebuild.
- Wrapping the Widget Tree: The InheritedWidget is used to wrap the widget tree that needs access to the counter.
- Accessing Data: Descendant widgets access the counter using the of(context) method, and they automatically rebuild when the counter changes.
Example 2: With Multiple Parameters
This amazing code shows how you can use InheritedWidget Flutter to manage and share multiple parameters in your app. It’s a simple and practical approach that can be expanded for more complex scenarios. See below example and its explanation.
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class AppSettingsInheritedWidget extends InheritedWidget { final String language; final String theme; const AppSettingsInheritedWidget({ Key? key, required this.language, required this.theme, required Widget child, }) : super(key: key, child: child); // Static method to easily access the InheritedWidget data static AppSettingsInheritedWidget? of(BuildContext context) { return context .dependOnInheritedWidgetOfExactType<AppSettingsInheritedWidget>(); } // Method to determine when to notify dependent widgets to rebuild @override bool updateShouldNotify(AppSettingsInheritedWidget oldWidget) { return oldWidget.language != language || oldWidget.theme != theme; } } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return AppSettingsInheritedWidget( language: 'English', // Initial language setting theme: 'Light', // Initial theme setting child: MaterialApp( home: MyHomePage(), ), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { // Accessing data from the InheritedWidget final settings = AppSettingsInheritedWidget.of(context); final language = settings?.language ?? 'Unknown'; final theme = settings?.theme ?? 'Default'; return Scaffold( appBar: AppBar(title: Text('InheritedWidget Multiple Parameters Example')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Language: $language', style: TextStyle(fontSize: 24)), Text('Theme: $theme', style: TextStyle(fontSize: 24)), ], ), ), ); } }

Explanation
1. AppSettingsInheritedWidget
- This class is an extension of InheritedWidget and holds both the language and theme settings. It’s designed to manage and share these settings across the app.
- The updateShouldNotify method checks if there’s a change in either the language or theme. If there’s a change, it triggers a rebuild of the dependent widgets.
2. MyApp
- In the MyApp class, we wrap the widget tree with AppSettingsInheritedWidget. We also set the initial values for the language (set to ‘English’) and theme (set to ‘Light’) inside the widget.
3. MyHomePage
- This is the widget that accesses the language and theme settings provided by AppSettingsInheritedWidget. It uses the of(context) method to get these values and displays them on the screen using Text widgets.
4. How it Works
- AppSettingsInheritedWidget: It manages and holds global settings like language and theme, making them accessible throughout the app.
- Accessing Data: Any widget under AppSettingsInheritedWidget (like MyHomePage) can easily access these settings by calling AppSettingsInheritedWidget.of(context).
- Rebuild Mechanism: Whenever the language or theme changes, any widget that depends on these settings automatically rebuilds to reflect the updated values.
Example 3: Sample User Authentication State Management with InheritedWidget
The code example we implemented below is simple yet powerful for managing and sharing state across widgets in Flutter using InheritedWidget. This makes state management easy and clear, without adding unnecessary code.
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class AuthInheritedWidget extends InheritedWidget { final bool isLoggedIn; final Function() toggleLoginStatus; const AuthInheritedWidget({ Key? key, required this.isLoggedIn, required this.toggleLoginStatus, required Widget child, }) : super(key: key, child: child); // Accessing the InheritedWidget data static AuthInheritedWidget? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<AuthInheritedWidget>(); } @override bool updateShouldNotify(AuthInheritedWidget oldWidget) { return oldWidget.isLoggedIn != isLoggedIn; // Rebuild if the login state changes } } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { bool _isLoggedIn = false; void _toggleLoginStatus() { setState(() { _isLoggedIn = !_isLoggedIn; // Toggle login state }); } @override Widget build(BuildContext context) { return AuthInheritedWidget( isLoggedIn: _isLoggedIn, // Passing the login state toggleLoginStatus: _toggleLoginStatus, // Passing the toggle function child: MaterialApp( home: MyHomePage(), ), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { // Accessing the login state and the toggle function final auth = AuthInheritedWidget.of(context); final isLoggedIn = auth?.isLoggedIn ?? false; final toggleLoginStatus = auth?.toggleLoginStatus; return Scaffold( appBar: AppBar(title: Text('User Authentication with InheritedWidget')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ // Display login/logout status Text(isLoggedIn ? 'You are logged in' : 'You are logged out', style: TextStyle(fontSize: 24)), ElevatedButton( onPressed: toggleLoginStatus, // Toggle login/logout child: Text(isLoggedIn ? 'Log out' : 'Log in'), ), ], ), ), ); } }
Explanation
1. AuthInheritedWidget
- This class manages the authentication state (isLoggedIn) and provides a method (toggleLoginStatus) to change that state.
- The updateShouldNotify method ensures that when the isLoggedIn value changes, any widgets depending on it (like MyHomePage) will automatically rebuild to reflect the new state.
2. MyApp
- This is the root widget that holds the authentication state (_isLoggedIn) and wraps the widget tree with AuthInheritedWidget.
- The login state and the method to toggle the state are passed down to the widget tree, making them accessible to any descendant widgets.
3. MyHomePage
- This widget accesses the authentication state (isLoggedIn) and the method (toggleLoginStatus) using AuthInheritedWidget.of(context).
- Based on the authentication state, it displays either a “Log in” or “Log out” button, allowing the user to toggle the login state.
4. How it Works
- State Management: The AuthInheritedWidget holds and manages the isLoggedIn state, and provides a function to update it.
- Rebuild Trigger: When toggleLoginStatus is called, the state changes, and updateShouldNotify triggers dependent widgets to rebuild, reflecting the updated state.
- Data Access: Any widget, like MyHomePage, can access the authentication state and toggle function by calling AuthInheritedWidget.of(context).
Example 4: Handling Configuration Data with Flutter Inherited Widget
This code will show you how you can efficiently manage and share configuration data, such as theme and language settings, across your app using InheritedWidget. For your code, it will make sure that all widgets relying on this data automatically rebuild when the settings are updated.
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class ConfigInheritedWidget extends InheritedWidget { final String theme; final String language; final Function(String) updateTheme; final Function(String) updateLanguage; const ConfigInheritedWidget({ Key? key, required this.theme, required this.language, required this.updateTheme, required this.updateLanguage, required Widget child, }) : super(key: key, child: child); static ConfigInheritedWidget? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<ConfigInheritedWidget>(); } @override bool updateShouldNotify(ConfigInheritedWidget oldWidget) { return oldWidget.theme != theme || oldWidget.language != language; } } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String _theme = 'Light'; String _language = 'English'; void _updateTheme(String newTheme) { setState(() { _theme = newTheme; }); } void _updateLanguage(String newLanguage) { setState(() { _language = newLanguage; }); } @override Widget build(BuildContext context) { return ConfigInheritedWidget( theme: _theme, language: _language, updateTheme: _updateTheme, updateLanguage: _updateLanguage, child: MaterialApp( home: MyHomePage(), ), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { final config = ConfigInheritedWidget.of(context); final theme = config?.theme ?? 'Light'; final language = config?.language ?? 'English'; final updateTheme = config?.updateTheme; final updateLanguage = config?.updateLanguage; return Scaffold( appBar: AppBar(title: Text('App Settings with InheritedWidget')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Current Theme: $theme', style: TextStyle(fontSize: 24)), Text('Current Language: $language', style: TextStyle(fontSize: 24)), ElevatedButton( onPressed: () { updateTheme?.call(theme == 'Light' ? 'Dark' : 'Light'); }, child: Text('Toggle Theme'), ), ElevatedButton( onPressed: () { updateLanguage ?.call(language == 'English' ? 'Spanish' : 'English'); }, child: Text('Toggle Language'), ), ], ), ), ); } }
Explanation
1. ConfigInheritedWidget
- This widget holds and manages the theme and language settings.
- It provides functions like updateTheme and updateLanguage to update these settings.
- updateShouldNotify ensures that when these settings change, dependent widgets are rebuilt.
2. MyApp
- The root widget where the settings (_theme and _language) are initialized and updated.
- ConfigInheritedWidget wraps the widget tree, making the settings available to all descendant widgets.
3. MyHomePage
- This widget accesses the configuration data (theme and language) using ConfigInheritedWidget.of(context).
- It displays the current settings and provides buttons to update both the theme and language. When these buttons are pressed, updateTheme and updateLanguage are called to change the settings.
4. How it Works
State Management
The ConfigInheritedWidget manages the app-wide settings (theme and language) and provides methods to update them.Rebuild Trigger
When either the theme or language changes (via updateTheme or updateLanguage), updateShouldNotify is triggered, causing dependent widgets to rebuild with the updated settings.Data Access and Update
Any widget, such as MyHomePage, can easily access and update these settings using ConfigInheritedWidget.of(context).
Conclusion
In this article, we’ve practically discussed how to use InheritedWidget in Flutter to manage and share data across your app effectively. We’ve looked at how it can handle both simple and more complex states, like authentication, configuration settings, and more. We explained with with multiple code examples so you can understand it better.
By wrapping parts of your widget tree with InheritedWidget, you ensure that any changes to shared data automatically trigger rebuilds of dependent widgets. This keeps your app’s UI consistent and up-to-date, without the need to pass data through constructors constantly. It’s a clean, efficient way to manage state across your app.
Whether you’re working on something simple, like a counter, or more complex data, like user preferences or app-wide settings, InheritedWidget is really good and powerful tool. It’s a great place to start for simpler state management, and as your app grows, you can dive into more advanced options like Provider or Riverpod.
Using InheritedWidget improves both the clarity and efficiency of your app’s state management that makes it easier to maintain and making sure solid performance for your Flutter applications.
If you have any issues then contact us and if want to learn more then visit our other amazing posts on Flutter widgets, state management, and more.