On the first code

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter InheritWidget', home: Scaffold( appBar: AppBar(), body: Center( child: BodyWidget(), ), ), ); }}class BodyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    returnBodyWidgetState(); }}class BodyWidgetState extends State<BodyWidget> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    print("BodyWidgetState build:$hashCode");
    return CustomInheritedWidget(
      data: count,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          DependOnInheritedWidget<int>(),
          Builder(builder: (context) {
            return CustomRaisedButton(
              onPressed: () {
                setState(() {
                  count++;
                });
              },
              child: Text("Digital + 1")); })],),); }}class CustomRaisedButton extends RaisedButton {
  const CustomRaisedButton({
    @required VoidCallback onPressed,
    Widget child,
  }) : super(onPressed: onPressed, child: child);

  @override
  Widget build(BuildContext context) {
    print("CustomRaisedButton build:$hashCode");
    return super.build(context); }}class DependOnInheritedWidget<T> extends StatefulWidget {

  DependOnInheritedWidget(){
    print("DependOnInheritedWidget:$hashCode");
  }

  @override
  State<StatefulWidget> createState() {
    returnDependOnInheritedWidgetState<T>(); }}class DependOnInheritedWidgetState<T> extends State<DependOnInheritedWidget> {
  @override
  Widget build(BuildContext context) {
    print("DependOnInheritedWidgetState build:$hashCode");
    return Text(CustomInheritedWidget.of<T>(context).data.toString());
  }

  @override
  void didChangeDependencies() {
    print("DependOnInheritedWidgetState didChangeDependencies:$hashCode");
    super.didChangeDependencies(); }}class CustomInheritedWidget<T> extends InheritedWidget {
  CustomInheritedWidget({Key key, this.data, Widget child}) : super(key: key, child: child);

  final T data;

  // Define a convenient method for widgets in a subtree to get shared data
  static CustomInheritedWidget<T> of<T>(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CustomInheritedWidget<T>>();
  }

  @override
  bool updateShouldNotify(CustomInheritedWidget oldWidget) {
    return oldWidget.data != data;
  }
}
Copy the code

At the beginning, the text is 0. When the button is clicked, the value is +1 and the page is refreshed.

I/flutter (24452): BodyWidgetState build:293287865
I/flutter (24452): DependOnInheritedWidget:698685804
I/flutter (24452): DependOnInheritedWidgetState didChangeDependencies:185018106
I/flutter (24452): DependOnInheritedWidgetState build:185018106
I/flutter (24452): CustomRaisedButton build:266734984
Copy the code

Through print log we find DependOnInheritedWidgetState call didChangeDependencies () method, we have to analyse, what happened when click on the button.

_inheritedWidgetsThe transfer

When Element is activated and mounted, the _updateInheritance method is called to direct _inheritedWidgets to _inheritedWidgets of _parent. The InheritedElement overrides this method, and the code looks like this

Mount () is the first time an element is added to the Element Tree, and then the element becomes active. Activate () re-marks the deactivate element as active.

In short, from nothing to something or from invisible to visible.

//Element
  void _updateInheritance() {
    assert(_active); _inheritedWidgets = _parent? ._inheritedWidgets; }Copy the code
//InheritedElement
 @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;
 }
Copy the code

[InheritedElement] InheritedElement copies _parent’s _inheritedWidgets and saves the widget’s runtimeType as key and this as value in its inheritedWidgets property. So the UI tree of the original code looks something like this

BodyWidget

-CustomInheritedWidget

–Column

—DependOnInheritedWidget

—Builder

—-CustomRaisedButton

CustomInheritedWidget copies the BodyWidget’s _inheritedWidgets into its own _inheritedWidgets and saves itself in it, Then the _inheritedWidgets of Column, DependOnInheritedWidget, Builder, and CustomRaisedButton point directly to the _inheritedWidget of CustomInheritedWidget s

InheritedElementThe search for

Call in DependOnInheritedWidgetState CustomInheritedWidget of method, Of method invocation context. DependOnInheritedWidgetOfExactType < CustomInheritedWidget < T > > () can find CustomInheritedWidget, code is as follows:

//Element
 @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;
 }

 @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
//InheritedElement
@protected
void updateDependencies(Element dependent, Object aspect) {
  setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object value) {
  _dependents[dependent] = value;
}
Copy the code

We can see that in dependOnInheritedWidgetOfExactType this method, the generic Widget to find corresponding InheritedElement, save the ancestor variable, DependOnInheritedWidget add its ancestor to the _dependencies collection and save itself to the _dependents collection where the InheritedElement updates its internal data. You can notify all widgets that rely on this InheritedElement. The DependOnInheritedWidget’s _dependencies collection has its ancestor added, so when the DependOnInheritedWidget is destroyed, You can remove yourself (DependOnInheritedWidget) from the _dependents collection of ancestors to avoid unnecessary updates and memory leaks.

conclusion

When Element is activated and mounted, the _updateInheritance method is called to direct _inheritedWidgets to _inheritedWidgets of _parent. InheritedElement overrides this method, saving itself in _inheritedWidgets, so that when you’re passing down the hierarchy, the _inheritedWidgets contain all of the inheritedElements in the process. Then when obtained through dependOnInheritedWidgetOfExactType InheritedElement method the caller to hold yourself to a InheritedElement _dependencies collection, In addition, the method caller adds a reference to the InheritedElement in its own _dependencies collection, and deletes itself from the InheritedElement’s _dependencies collection when the method caller destroys it. Avoid unnecessary updates and memory leaks.

To consider

  1. ElementtheactivateandmountWhen is the method called?
  2. inCustomInheritedWidgetWhen changing internal data, whyDependOnInheritedWidgetStateWill be calleddidChangeDependencies()?