Flutter InheritedWidget

Share data down the widget tree efficiently

🌳 What is InheritedWidget?

InheritedWidget is Flutter's low-level mechanism for propagating data down the widget tree. It allows child widgets to access shared data without passing it through constructors, making state management more efficient.


// Access inherited data
final data = MyInheritedWidget.of(context);
                                    

Key Concepts

📡

Data Propagation

Share data down the tree

class MyInherited 
  extends InheritedWidget
🎯

Efficient Updates

Only rebuilds dependent widgets

@override
bool updateShouldNotify()
🔍

Context Access

Find data using context

MyWidget.of(context)
🏗️

Foundation

Base for other state solutions

// Provider uses this
// Theme uses this

🔹 Basic InheritedWidget Example

Create a simple theme provider:

import 'package:flutter/material.dart';

// 1. Create InheritedWidget
class ThemeProvider extends InheritedWidget {
  final Color primaryColor;
  final Widget child;

  ThemeProvider({
    required this.primaryColor,
    required this.child,
  }) : super(child: child);

  // 2. Provide access method
  static ThemeProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ThemeProvider>();
  }

  // 3. Determine when to notify
  @override
  bool updateShouldNotify(ThemeProvider oldWidget) {
    return primaryColor != oldWidget.primaryColor;
  }
}

// 4. Use in widget tree
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      primaryColor: Colors.blue,
      child: MaterialApp(
        home: HomePage(),
      ),
    );
  }
}

Output:

A theme provider that shares a primary color with all child widgets.

🔹 Accessing Inherited Data

Child widgets can access the shared data:

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Access the inherited data
    final theme = ThemeProvider.of(context);
    
    return Scaffold(
      appBar: AppBar(
        title: Text('InheritedWidget Demo'),
        backgroundColor: theme?.primaryColor,
      ),
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          color: theme?.primaryColor,
          child: Center(
            child: Text(
              'Themed Box',
              style: TextStyle(color: Colors.white),
            ),
          ),
        ),
      ),
    );
  }
}

Output:

A page with an AppBar and container both using the inherited primary color.

🔹 Counter with InheritedWidget

Manage counter state across widgets:

class CounterProvider extends InheritedWidget {
  final int counter;
  final Function() increment;
  final Widget child;

  CounterProvider({
    required this.counter,
    required this.increment,
    required this.child,
  }) : super(child: child);

  static CounterProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CounterProvider>();
  }

  @override
  bool updateShouldNotify(CounterProvider oldWidget) {
    return counter != oldWidget.counter;
  }
}

// Wrapper with state
class CounterWrapper extends StatefulWidget {
  final Widget child;
  CounterWrapper({required this.child});

  @override
  _CounterWrapperState createState() => _CounterWrapperState();
}

class _CounterWrapperState extends State<CounterWrapper> {
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return CounterProvider(
      counter: _counter,
      increment: _increment,
      child: widget.child,
    );
  }
}

Output:

A counter provider that shares counter value and increment function with all descendants.

🔹 Using the Counter Provider

Access counter from any child widget:

class CounterDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final provider = CounterProvider.of(context);
    
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Count: ${provider?.counter}',
          style: TextStyle(fontSize: 32),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: provider?.increment,
          child: Text('Increment'),
        ),
      ],
    );
  }
}

Output:

A widget displaying the counter value and a button to increment it.

🔹 When to Use InheritedWidget

✅ Good For:

  • Sharing data across multiple widgets
  • Theme and configuration data
  • Building custom state management
  • Learning Flutter internals
  • Performance-critical applications

❌ Not Ideal For:

  • Beginners (use Provider instead)
  • Complex state logic
  • When you need middleware
  • Apps requiring time-travel debugging

🔹 Important Notes

Key Points:

  • Low-level: InheritedWidget is the foundation for Provider, Theme, and MediaQuery
  • Immutable: Data in InheritedWidget should be immutable
  • Rebuild control: Use updateShouldNotify() to control rebuilds
  • Context dependency: Widgets using .of(context) will rebuild when data changes
  • Not for beginners: Consider using Provider package for easier state management

🧠 Test Your Knowledge

What method controls when InheritedWidget notifies dependents?