Before starting this article, if you haven’t read the previous articles, I suggest you read the following. Start with the StatelessWidget and enter the world of StatefulWidgets

In 2020 I completed an open source film project for Flutter as follows.

The App project of Flutter is based on Provider+MVVM

If you’re not familiar with the ProxyWidget class, you probably know that InheritedWidget is subclass InheritedWidget. It is one of the soul Settings in the Flutter Widget because the InheritedWidget is often used to do data sharing. For example, common Theme/ThemeData, Text/DefaultTextStyle, etc. are all shared using InheritedWidget, and the state management framework of Flutter is also implemented using it. For example, one of the most well-known state management frameworks is Provider.

ProxyWidget

ProxyWidget is the parent of the InheritedWidget, so what does it do? A widget that has A child widget provided to it, instead of building A new widget. The idea is that this is a part that provides child parts, rather than building new parts.

ProxyWidget source code:

abstract class ProxyWidget extends Widget {
  /// Creates a widget that has exactly one child widget.
  const ProxyWidget({ Key key, @required this.child }) : super(key: key);

  /// The widget below this widget in the tree.
  ///
  /// {@template flutter.widgets.child}
  /// This widget can only have one child. To lay out multiple children, let this
  /// widget's child be a widget such as [Row], [Column], or [Stack], which have a
  /// `children` property, and then provide the children to that widget.
  /// {@endtemplate}
  final Widget child;
}
Copy the code

As you can see, the ProxyWidget abstract class is simple enough that it has only one child property inside it. So let’s move on to the inheritance. There are two subclasses InheritedWidget and ParentDataWidget that are subclasses of ProxyWidget. For those of you who have read the previous articles, it should be obvious to say Element after widget, because Element is really the method that provides rendering and widgets are just configuration items.

ProxyElement

/// An [Element] that uses a [ProxyWidget] as its configuration.
abstract class ProxyElement extends ComponentElement {
  /// Initializes fields for subclasses.
  ProxyElement(ProxyWidget widget) : super(widget);

  @override
  ProxyWidget get widget => super.widget as ProxyWidget;

  @override
  Widget build() => widget.child;

  @override
  void update(ProxyWidget newWidget) {
    final ProxyWidget oldWidget = widget;
    assert(widget ! =null);
    assert(widget ! = newWidget);super.update(newWidget);
    assert(widget == newWidget);
    updated(oldWidget);
    _dirty = true;
    rebuild();
  }

  @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  }

element.
  @protected
  void notifyClients(covariant ProxyWidget oldWidget);
}
   
Copy the code

Update, updated, and notifyClients are provided. The update method calls the updated method and the rebuild method is marked clean. The updated method calls the notifyClient method. Typically, subclasses will override it to implement their own logic before calling their parent’s updated method. The notifyClients method is an abstract method that is implemented by subclasses. (This will be mentioned later when analyzing subclasses.)

InheritedWidget

InheritedWidget is a subclass of the Implementation of The ProxyWidget and plays an important role in Flutter as described above. Let’s take a look at the source code as follows:

   abstract class InheritedWidget extends ProxyWidget {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const InheritedWidget({ Key key, Widget child })
    : super(key: key, child: child);

  @override
  InheritedElement createElement() => InheritedElement(this);

  /// 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);
}
Copy the code

The interior is fairly simple. The first thing you need to do is create its corresponding Element, and then provide an abstract method called updateShouldNotify. This method is used to control whether or not data is synchronized, that is, whether or not a child component needs to be rebuilt if its value changes when referencing the InheritedWidget’s parent component.

InheritedElement

class InheritedElement extends ProxyElement {
  /// Creates an element that uses the given widget as its configuration.
  InheritedElement(InheritedWidget widget) : super(widget);

  @override
  InheritedWidget get widget => super.widget as InheritedWidget;

  final Map<Element.Object> _dependents = HashMap<Element.Object> ();@override
  void _updateInheritance() {
    assert(_active);
    final Map<Type, InheritedElement> incomingWidgets = _parent? ._inheritedWidgets;if(incomingWidgets ! =null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap<Type, InheritedElement>();
    _inheritedWidgets[widget.runtimeType] = this;
  }

  @override
  void debugDeactivated() {
    assert(() {
      assert(_dependents.isEmpty);
      return true; } ());super.debugDeactivated();
  }

  @protected
  Object getDependencies(Element dependent) {
    return _dependents[dependent];
  }


  @protected
  void setDependencies(Element dependent, Object value) {
    _dependents[dependent] = value;
  }


  @protected
  void updateDependencies(Element dependent, Object aspect) {
    setDependencies(dependent, null);
  }


  @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }


  @override
  void updated(InheritedWidget oldWidget) {
    if (widget.updateShouldNotify(oldWidget))
      super.updated(oldWidget);
  }

  @override
  void notifyClients(InheritedWidget oldWidget) {
    assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
    for (final Element dependent in _dependents.keys) {
      assert(() {
        // check that it really is our descendant
        Element ancestor = dependent._parent;
        while(ancestor ! =this&& ancestor ! =null)
          ancestor = ancestor._parent;
        return ancestor == this; } ());// check that it really depends on us
      assert(dependent._dependencies.contains(this)); notifyDependent(oldWidget, dependent); }}}Copy the code

We will see that the “_inheritedWidgets” map is inside the parent Element. We will start with the StatelessWidget. So what is it doing with the InheritedElement? Save the current InheritedWidget object. Why save it? Again, the InheritedWidget is used to find the parent class using the child component’s context. We want to use InheritedWiget inside data, flutter he provides several methods for us, one way is to “context. GetElementForInheritedWidgetOfExactType ()”. It needs to use _inheritedWidgets to get the InheritedWidget object it wants.

Gets the InheritedWidget object for the parent component














  @override
  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    return ancestor;
  }
Copy the code

Just get the InheritedWidget object without doing anything else as described above. So it’s just getting objects.

Look at the “dependOnInheritedWidgetOfExactType” source code:

  @override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    if(ancestor ! =null) {
      assert(ancestor is InheritedElement);
      return dependOnInheritedElement(ancestor, aspect: aspect) as T;
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }
Copy the code

[inheritedWidgets] [inheritedWidgets] [inheritedWidgets] [inheritedWidgets] But unlike “getElementForInheritedWidgetOfExactType” method then calls “dependOnInheritedElement (ancestor, the aspect: the aspect) as T” method. Take a look at its source code:

  @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor ! =null); _dependencies ?? = HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
Copy the code

Internally, we call the updateDependencies method on the InheritedElement to pass in the current elment.

  @protected
  void updateDependencies(Element dependent, Object aspect) {
    setDependencies(dependent, null);
  }
Copy the code

The inside of the method then calls the setDependencies method to pass the current Element along.

  @protected
  void setDependencies(Element dependent, Object value) {
    _dependents[dependent] = value;
  }
Copy the code

Internally, the setDependencies method saves the current elment as a _dependents HashMap. final Map

_dependents = HashMap

();
,>
,>

The InheritedWidget updates the data

  @override
  void updated(InheritedWidget oldWidget) {
    if (widget.updateShouldNotify(oldWidget))
      super.updated(oldWidget);
  }
Copy the code

Note that the updated method of the parent class needs to be called “updateShouldNotify” before calling its updated method. This method is an abstract method of the InheritedWidget and requires its subclasses to implement it. This method also controls whether the data is synchronized, so that the operation can be controlled by subclasses. What does the updated method that calls parent ProxyElement do?

  @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  }
  
    @protected
  void notifyClients(covariant ProxyWidget oldWidget);
Copy the code

The updated method on the InheritedElement calls notifyClients, which is an abstract method implemented by subclasses.

  @override
  void notifyClients(InheritedWidget oldWidget) {
    assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
    for (final Element dependent in _dependents.keys) {
      assert(() {
        // check that it really is our descendant
        Element ancestor = dependent._parent;
        while(ancestor ! =this&& ancestor ! =null)
          ancestor = ancestor._parent;
        return ancestor == this; } ());// check that it really depends on us
      assert(dependent._dependencies.contains(this)); notifyDependent(oldWidget, dependent); }}Copy the code

It calls the “notifyDependent” method through the current InheritedElement object of “_dependencies”.

  @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }
Copy the code

Internally, Element’s “didChangeDependencies” method is called. This is why the child component’s didChangeDependencies method is called when it relies on InheritedWidget data, and markNeedsBuild is re-built in its method. At this point, the source code analysis of the InheritedWidget is complete.

plan