flutterinherited-widget

Does the InheritedWidget's updateShouldNotify need to exist?


The official comment of updateShouldNotify says it can control the InheritedWidget's child build or not when the InheritedWidget is rebuild.

  /// Whether the framework should notify widgets that inherit from this widget.
  ///
  /// When this widget is rebuilt, sometimes we need to rebuild the widgets that
  /// inherit from this widget but sometimes we do not. For example, if the data
  /// held by this widget is the same as the data held by `oldWidget`, then we
  /// do not need to rebuild the widgets that inherited the data held by
  /// `oldWidget`.
  ///
  /// The framework distinguishes these cases by calling this function with the
  /// widget that previously occupied this location in the tree as an argument.
  /// The given widget is guaranteed to have the same [runtimeType] as this
  /// object.


@protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);

But if the InheritedWidget is rebuilt , then all the widgets inherited from the this widget will rebuilt no matter this updateShouldNotify is return false or true. because these widgets are the children of the InheritedWidget .

Can anybody give an example of children do not rebuild with updateShouldNotify when the InheritedWidget is rebuilt?


Solution

  • I can give you a simple example: how can a const child widget of the InheritedWidget ever be rebuilt if it is never notified?

    Run this sample code to see the behavior. Change the return value of updateShouldNotify from false to true and then tap on the button.

    /// This is the [StatefulWidget] that will rebuild the [InheritedWidget].
    class InheritedParent extends StatefulWidget {
      const InheritedParent({super.key});
    
      @override
      State<InheritedParent> createState() => _InheritedParentState();
    }
    
    class _InheritedParentState extends State<InheritedParent> {
      @override
      Widget build(BuildContext context) {
        return Inherited(
          /// Pass the random color to the [InheritedWidget].
          color: _getRandomColor(),
          child: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                /// The inherited child is constant.
                const InheritedChild(),
                FloatingActionButton(
                  /// We call setState, so the entire subtree should be rebuilt...
                  /// but what about 'const InheritedChild()'?
                  onPressed: () => setState(() {}),
                  child: const Icon(Icons.add),
                ),
              ],
            ),
          ),
        );
      }
    
      /// This method will return a random color.
      Color _getRandomColor() {
        return Colors.primaries[math.Random().nextInt(Colors.primaries.length)];
      }
    }
    
    /// The widget that provides a [Color] to its descendants through the context.
    class Inherited extends InheritedWidget {
      const Inherited({
        super.key,
        required this.color,
        required super.child,
      });
    
      final Color color;
    
      static Inherited? of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType<Inherited>();
      }
    
      @override
      bool updateShouldNotify(Inherited oldWidget) {
        /// Change this to see the behavior.
        return false;
      }
    }
    
    class InheritedChild extends StatefulWidget {
      const InheritedChild({super.key});
    
      @override
      State<InheritedChild> createState() => _InheritedChildState();
    }
    
    class _InheritedChildState extends State<InheritedChild> {
      @override
      Widget build(BuildContext context) {
        return Container(
          width: 128.0,
          height: 128.0,
          /// Access the color specified in 'Inherited'.
          color: Inherited.of(context)?.color,
        );
      }
    }
    

    The InheritedWidget allows you to filter rebuilds (calls to setState so that descendants are only rebuilt when any data that they depend on (from the parent InheritedWidget) changes.

    While the convenience idiom is to provide a static of(context) method for your inherited widgets, remember that they can be used without one; to make it explicit:

    class _InheritedChildState extends State<InheritedChild> {
      @override
      Widget build(BuildContext context) {
        return Container(
          width: 128.0,
          height: 128.0,
          /// This widget DEPENDS on the [Inherited] to rebuild.
          color: context.dependOnInheritedWidgetOfExactType<Inherited>()?.color,
        );
      }
    }
    

    From the documentation: "Returns the nearest widget of the given type T and creates a dependency on it".

    When a dependency is created, the child widget is registered for changes. When does it get notified of the changes?

    When updateShouldNotify return true.

    Therefore, the correct implementation of Inherited.updateShouldNotify is:

    @override
    bool updateShouldNotify(Inherited oldWidget) {
      /// The data that the child widget depends on in order to properly
      /// build itself.
      return oldWidget.color != color;
    }