What is inherited widget in flutter and what are all the use cases of it
#flutter#widget
Answer
InheritedWidget in Flutter
InheritedWidget is a special Flutter widget that efficiently propagates data down the widget tree, allowing descendant widgets to access shared data without passing it through constructors.
What is InheritedWidget?
InheritedWidget is the foundation of Flutter's dependency injection system and the base for popular state management solutions like Provider.
Basic Implementation
dart// Data class to share class AppData { final String theme; final String username; AppData({required this.theme, required this.username}); } // InheritedWidget implementation class AppDataWidget extends InheritedWidget { final AppData data; const AppDataWidget({ Key? key, required this.data, required Widget child, }) : super(key: key, child: child); // Accessor method for descendants static AppDataWidget? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<AppDataWidget>(); } // Determines if dependents should be notified of changes bool updateShouldNotify(AppDataWidget oldWidget) { return oldWidget.data.theme != data.theme || oldWidget.data.username != data.username; } } // Usage in widget tree class MyApp extends StatelessWidget { Widget build(BuildContext context) { return AppDataWidget( data: AppData(theme: 'dark', username: 'John'), child: MaterialApp( home: HomeScreen(), ), ); } } // Accessing data in descendant widget class HomeScreen extends StatelessWidget { Widget build(BuildContext context) { final appData = AppDataWidget.of(context)?.data; return Scaffold( body: Center( child: Text('Welcome ${appData?.username}!'), ), ); } }
Advanced Example with State Management
dart// Mutable data holder class CounterState { final int count; CounterState(this.count); CounterState copyWith({int? count}) { return CounterState(count ?? this.count); } } // InheritedWidget for state class CounterInherited extends InheritedWidget { final CounterState state; final Function(int) onIncrement; const CounterInherited({ Key? key, required this.state, required this.onIncrement, required Widget child, }) : super(key: key, child: child); static CounterInherited? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<CounterInherited>(); } bool updateShouldNotify(CounterInherited oldWidget) { return oldWidget.state.count != state.count; } } // StatefulWidget wrapper class CounterProvider extends StatefulWidget { final Widget child; const CounterProvider({Key? key, required this.child}) : super(key: key); _CounterProviderState createState() => _CounterProviderState(); } class _CounterProviderState extends State<CounterProvider> { CounterState _state = CounterState(0); void _increment(int value) { setState(() { _state = _state.copyWith(count: _state.count + value); }); } Widget build(BuildContext context) { return CounterInherited( state: _state, onIncrement: _increment, child: widget.child, ); } } // Using the provider class CounterScreen extends StatelessWidget { Widget build(BuildContext context) { final counter = CounterInherited.of(context); return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Count: ${counter?.state.count}', style: TextStyle(fontSize: 32), ), ElevatedButton( onPressed: () => counter?.onIncrement(1), child: Text('Increment'), ), ], ), ), ); } }
Use Cases
1. Theme Management
dartclass ThemeInherited extends InheritedWidget { final ThemeData theme; final Function(ThemeData) updateTheme; const ThemeInherited({ required this.theme, required this.updateTheme, required Widget child, }) : super(child: child); static ThemeInherited? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<ThemeInherited>(); } bool updateShouldNotify(ThemeInherited oldWidget) { return oldWidget.theme != theme; } } // Usage class ThemedButton extends StatelessWidget { Widget build(BuildContext context) { final themeData = ThemeInherited.of(context)?.theme; return ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: themeData?.primaryColor, ), onPressed: () {}, child: Text('Themed Button'), ); } }
2. User Authentication
dartclass AuthInherited extends InheritedWidget { final User? currentUser; final bool isAuthenticated; final Future<void> Function(String, String) login; final Future<void> Function() logout; const AuthInherited({ required this.currentUser, required this.isAuthenticated, required this.login, required this.logout, required Widget child, }) : super(child: child); static AuthInherited? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<AuthInherited>(); } bool updateShouldNotify(AuthInherited oldWidget) { return oldWidget.currentUser != currentUser || oldWidget.isAuthenticated != isAuthenticated; } }
3. Localization
dartclass LocalizationInherited extends InheritedWidget { final Locale locale; final Map<String, String> translations; const LocalizationInherited({ required this.locale, required this.translations, required Widget child, }) : super(child: child); static LocalizationInherited? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<LocalizationInherited>(); } String translate(String key) { return translations[key] ?? key; } bool updateShouldNotify(LocalizationInherited oldWidget) { return oldWidget.locale != locale; } }
4. Configuration/Settings
dartclass ConfigInherited extends InheritedWidget { final String apiUrl; final bool debugMode; final Map<String, dynamic> settings; const ConfigInherited({ required this.apiUrl, required this.debugMode, required this.settings, required Widget child, }) : super(child: child); static ConfigInherited? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<ConfigInherited>(); } bool updateShouldNotify(ConfigInherited oldWidget) { return oldWidget.apiUrl != apiUrl || oldWidget.debugMode != debugMode || oldWidget.settings != settings; } }
Performance Optimization
Selective Rebuilding:
dartclass OptimizedInherited extends InheritedWidget { final ValueNotifier<int> counter; const OptimizedInherited({ required this.counter, required Widget child, }) : super(child: child); static OptimizedInherited? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<OptimizedInherited>(); } bool updateShouldNotify(OptimizedInherited oldWidget) { // Only notify if counter value changed return oldWidget.counter.value != counter.value; } } // Widget that rebuilds only when needed class SelectiveRebuildWidget extends StatelessWidget { Widget build(BuildContext context) { final inherited = OptimizedInherited.of(context); return ValueListenableBuilder<int>( valueListenable: inherited!.counter, builder: (context, value, child) { return Text('Count: $value'); }, ); } }
Comparison with Other Solutions
| Feature | InheritedWidget | Provider | Riverpod |
|---|---|---|---|
| Complexity | High | Medium | Medium |
| Boilerplate | High | Low | Low |
| Type Safety | Manual | Good | Excellent |
| Testing | Hard | Medium | Easy |
| Learning Curve | Steep | Moderate | Moderate |
| Performance | Excellent | Excellent | Excellent |
| Use Case | Foundation | General | Modern apps |
Best Practices
- Always implement correctlytext
updateShouldNotify() - Use constructors when possibletext
const - Don't rebuild unnecessarily
- Consider using Provider for simpler code
- Cache expensive computations
- Test with text
dependOnInheritedWidgetOfExactType
Common Pitfalls
dart// ❌ Bad: Missing null check final data = AppDataWidget.of(context).data; // May crash! // ✅ Good: Safe access final data = AppDataWidget.of(context)?.data; if (data != null) { // Use data } // ❌ Bad: Always returning true bool updateShouldNotify(covariant InheritedWidget oldWidget) { return true; // Rebuilds all dependents always! } // ✅ Good: Compare actual data bool updateShouldNotify(AppDataWidget oldWidget) { return oldWidget.data != data; }
Important: InheritedWidget is powerful but low-level. For most apps, consider using Provider or Riverpod which build on InheritedWidget with less boilerplate and better ergonomics.
Learn more at InheritedWidget Documentation.