Skip to main content

Using InheritedWidget

1. Understanding InheritedWidget Concept

InheritedWidget is a Flutter widget that allows data to be efficiently shared between multiple widgets in the widget tree without requiring each widget to rebuild when the data changes.

Property Description Usage
updateShouldNotify Override this method to control when child widgets should rebuild.
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
  return oldWidget.data != data;
}
of Static method to access the nearest InheritedWidget of a given type. MyInheritedWidget.of(context)

Example: Basic InheritedWidget Usage

class MyInheritedWidget extends InheritedWidget {
  final String data;

  const MyInheritedWidget({
    required this.data,
    required Widget child,
  }) : super(child: child);

  static MyInheritedWidget of(BuildContext context) {
    return context.findAncestorWidgetOfExactType<MyInheritedWidget>()!;
  }

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return oldWidget.data != data;
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final data = MyInheritedWidget.of(context).data;
    return Text(data);
  }
}
Note: InheritedWidget is particularly useful for sharing theme data, localization, or other frequently accessed values across a widget tree.

2. Creating InheritedWidget Subclasses

Property Type Description
constructor InheritedWidget Creates a new InheritedWidget instance.
updateShouldNotify bool Function(InheritedWidget oldWidget) Override to control when dependent widgets should rebuild.

Example: Basic InheritedWidget Implementation

class ThemeData {
  final Color primaryColor;
  ThemeData({required this.primaryColor});
}

class ThemeInheritedWidget extends InheritedWidget {
  final ThemeData themeData;

  ThemeInheritedWidget({
    required this.themeData,
    required Widget child,
  }) : super(child: child);

  static ThemeData of(BuildContext context) {
    final ThemeInheritedWidget? result = 
        context.getElementForInheritedWidgetOfExactType<ThemeInheritedWidget>();
    return result?.themeData ?? ThemeData(primaryColor: Colors.blue);
  }

  @override
  bool updateShouldNotify(ThemeInheritedWidget oldWidget) {
    return themeData != oldWidget.themeData;
  }
}
Note: InheritedWidget is a powerful way to efficiently propagate data down the widget tree without requiring each descendant to explicitly receive the data.

3. Implementing updateShouldNotify() Method

Method Description Return Type Notes
updateShouldNotify() Override to control when widgets should rebuild bool Must return true to trigger rebuilds when data changes

Example: Basic Implementation

class MyInheritedWidget extends InheritedWidget {
  final String data;

  MyInheritedWidget({
    required Widget child,
    required this.data,
  }) : super(child: child);

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return data != oldWidget.data;
  }
}

Example: Advanced Implementation with Multiple Values

class UserInheritedWidget extends InheritedWidget {
  final User user;
  final Theme theme;

  UserInheritedWidget({
    required Widget child,
    required this.user,
    required this.theme,
  }) : super(child: child);

  @override
  bool updateShouldNotify(UserInheritedWidget oldWidget) {
    return user.id != oldWidget.user.id ||
           theme.name != oldWidget.theme.name;
  }
}
Note: The updateShouldNotify method is called whenever the parent widget rebuilds. It's crucial to implement it correctly to avoid unnecessary rebuilds.

4. Accessing InheritedWidget

Method Description Usage
InheritedWidget.of(context) Retrieves the nearest ancestor InheritedWidget of the specified type. final widget = MyInheritedWidget.of(context);
InheritedWidget.of(context, type) Retrieves the nearest ancestor of a specific type. final widget = InheritedWidget.of<MyInheritedWidget>(context);

Example: Accessing InheritedWidget in a Widget

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final inheritedData = MyInheritedWidget.of(context);
    return Text(inheritedData.data);
  }
}
Note: Ensure that the context is within a widget tree that has an ancestor of the desired InheritedWidget.

5. Using InheritedWidget.of(context) Method

Method Description Return Type Notes
InheritedWidget.of(context) Retrieves the nearest ancestor InheritedWidget of the specified type from the widget tree. InheritedWidget or null Used to access data from parent widgets that use InheritedWidget.

Example: Accessing InheritedWidget Data

class MyInheritedWidget extends InheritedWidget {
  final String data;

  MyInheritedWidget({required this.data, required Widget child}) : super(child: child);

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return oldWidget.data != data;
  }

  static MyInheritedWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>()!;
  }
}

class ConsumerWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final widgetData = MyInheritedWidget.of(context).data;
    return Text(widgetData);
  }
}
Note: The of method is a static helper that simplifies accessing the widget instance. It uses context.dependOnInheritedWidgetOfExactType internally.

6. Understanding BuildContext and Dependencies

Aspect Description Usage
BuildContext Represents the location of a widget in the widget tree. Used to access inherited widgets, themes, and other context-specific data.
Dependencies Tracks which widgets depend on specific data or services. Used for efficient re-rendering when dependencies change.

Example: Accessing BuildContext

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Accessing theme from BuildContext
    final theme = Theme.of(context);
    return Text(
      'Theme color: ${theme.primaryColor}',
      style: theme.textTheme.headlineMedium,
    );
  }
}

Example: Using InheritedWidget with BuildContext

class MyAppTheme extends InheritedWidget {
  final Color primaryColor;

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

  static MyAppTheme of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyAppTheme>()!;
  }

  @override
  bool updateShouldNotify(MyAppTheme oldWidget) {
    return primaryColor != oldWidget.primaryColor;
  }
}

class ThemeConsumer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final theme = MyAppTheme.of(context);
    return Container(
      color: theme.primaryColor,
      child: Text('Using inherited theme'),
    );
  }
}
Note: BuildContext is essential for accessing inherited widgets and managing widget dependencies efficiently.

Key Points

  • BuildContext provides access to the widget tree and inherited widgets.
  • Dependencies help Flutter optimize rebuilds by tracking widget dependencies.
  • InheritedWidget is a powerful way to share data across the widget tree efficiently.

7. Propagating Data Down Widget Tree

In Flutter, InheritedWidget is a powerful mechanism for efficiently propagating data down the widget tree. It allows data to be shared across multiple widgets without requiring each widget to explicitly receive the data as a parameter.

Property Description Usage
updateShouldNotify Override this method to control when widgets should rebuild when the data changes. @override bool updateShouldNotify(MyInheritedWidget oldWidget) { return oldWidget.data != data; }

Example: Basic InheritedWidget Implementation

class MyInheritedWidget extends InheritedWidget {
  final String data;

  const MyInheritedWidget({
    Key? key,
    required this.data,
    required Widget child,
  }) : super(key: key, child: child);

  static MyInheritedWidget of(BuildContext context) {
    final widget = context.getElementForInheritedWidgetOfExactType<MyInheritedWidget>();
    return widget as MyInheritedWidget;
  }

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return data != oldWidget.data;
  }
}

Example: Using InheritedWidget in a Widget Tree

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MyInheritedWidget(
      data: 'Shared Data',
      child: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final data = MyInheritedWidget.of(context).data;
    return Text(data);
  }
}
Note: InheritedWidget is particularly useful for sharing data like theme information, user preferences, or app-wide state without requiring widgets to pass this data explicitly down the tree.

8. Using InheritedNotifier Widget

The InheritedNotifier widget in Flutter is a specialized version of InheritedWidget designed to efficiently notify listeners when a ChangeNotifier or ValueNotifier changes. It's particularly useful for managing state that needs to be shared across multiple parts of the widget tree without requiring a full rebuild.

Property Description Usage
notifier The ChangeNotifier or ValueNotifier instance to observe. Required. Must be a valid notifier object.
child The child widget tree that will have access to the notifier's state. Optional. Defaults to null.

Example: Using InheritedNotifier to Share State

import 'package:flutter/material.dart';

class CounterNotifier extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

class CounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return InheritedNotifier(
      notifier: CounterNotifier(),
      child: Column(
        children: [
          Text("Count: ${CounterNotifier().count}"),
          ElevatedButton(
            onPressed: () {
              CounterNotifier().increment();
            },
            child: Text("Increment"),
          ),
        ],
      ),
    );
  }
}
Note: InheritedNotifier is optimized for performance and ensures that only widgets listening to the notifier are rebuilt when state changes.

9. Implementing Theme and Locale with InheritedWidget

Property Description Usage
InheritedWidget Base class for widgets that efficiently propagate data down the widget tree. Extend to create custom inherited widgets for theme or locale data.
updateShouldNotify Override to control when child widgets should rebuild. Return true if changes to the data require a rebuild.

Example: Custom Theme InheritedWidget

class CustomTheme extends InheritedWidget {
  final Color primaryColor;

  const CustomTheme({
    required this.primaryColor,
    required Widget child,
  }) : super(child: child);

  static CustomTheme of(BuildContext context) {
    return context.findAncestorWidgetOfExactType<CustomTheme>()!;
  }

  @override
  bool updateShouldNotify(CustomTheme old) => primaryColor != old.primaryColor;
}

Example: Using Theme in Widget Tree

class ThemeConsumer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final theme = CustomTheme.of(context);
    return Container(
      color: theme.primaryColor,
      child: Text('Themed Content'),
    );
  }
}

Example: Locale InheritedWidget

class LocaleProvider extends InheritedWidget {
  final Locale locale;

  const LocaleProvider({
    required this.locale,
    required Widget child,
  }) : super(child: child);

  static LocaleProvider of(BuildContext context) {
    return context.findAncestorWidgetOfExactType<LocaleProvider>()!;
  }

  @override
  bool updateShouldNotify(LocaleProvider old) => locale != old.locale;
}
Note: InheritedWidget is ideal for propagating immutable data like theme or locale efficiently down the widget tree.

10. Understanding Performance Implications

Aspect Description Impact
Widget Tree Rebuilds InheritedWidget changes trigger rebuilds of all descendant widgets that depend on it. Can cause unnecessary UI updates if not managed carefully.
Notification Propagation Changes to InheritedWidget values propagate down the tree, even if not directly used. May lead to performance degradation in large widget trees.
Memory Usage Each InheritedWidget instance holds a copy of its data, potentially increasing memory usage. High if many widgets depend on the same InheritedWidget with large data sets.
Note: Use const constructors where possible to reduce rebuilds and optimize performance.

Example: Efficient InheritedWidget Usage

class MyInheritedWidget extends InheritedWidget {
  final String data;

  const MyInheritedWidget({
    Key? key,
    required Widget child,
    required this.data,
  }) : super(key: key, child: child);

  static MyInheritedWidget of(BuildContext context) {
    final MyInheritedWidget? result = context
        .dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
    return result!;
  }

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return data != oldWidget.data;
  }
}

class ConsumerWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final data = MyInheritedWidget.of(context).data;
    return Text(data);
  }
}

Was this article helpful?