美文网首页
Flutter原理篇:didChangeDependencies

Flutter原理篇:didChangeDependencies

作者: 耗子_wo | 来源:发表于2021-12-19 01:02 被阅读0次

    各位,又到了我们原理篇的时间了,基于最近几篇flutter原理篇的文章,这一期应该是写paint重绘篇的内容的,但是最近在调试程序的时候遇到一个小的问题就是关于didChangeDependencies 方法什么时候被调用的有点搞不清楚,本着搞不清楚就上网查询的习惯,结果搜了好多篇文章发现要么就是内容非常简单说了和没说一样,要么就是写了一个Demo得出了一个没有细节经不住拷问的大概的结论(你也不能说他错,总觉得还是差了点什么),更有甚者就是千篇一律的拷贝内容文章(😁我想你应该懂我的意思),本着考究探索的精神发现了这些文章以后不惊要大呼一声:
    ”!这样下去是不对的。“
    自己必须要搞清楚写出来才算对得起大家,也对得起自己在flutter上面的探索,于是就有了今天这篇文章,好了废话不多说了,让我们进入正题来讨论下 《didChangeDependencies方法到底是什么时候被调用的》

    其实在普通我们写简单例子的时候我可以一句话说清楚didChangeDependencies方法在什么时候被调用,那就是在它的 State 对象对应的 ElementcreateElement() 的时候并且被Mount的时候就会被调用,简单就是说当这个对应的 Element 被重新创建的时候就会被调用

    你看到这里就会说不对啊,你这个结论和我写的Demo对不上啊,和网上一些结论也大相径庭啊,你先别着急先往下看,看看到底是怎么一回事

    我先上一个Demo,这个Demo也就是网上面传的比较广的,我们就以这个来举例子说明网上的结论:“父级结构中的层级发生变化时didChangeDependencies被调用“ 这个结论为什么是不完整

    import 'package:flutter/material.dart';
    
    class TestDidChangeDependencies2 extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => TestDidChangeDependencies2State();
    }
    
    class TestDidChangeDependencies2State extends State<TestDidChangeDependencies2> {
      bool bDependenciesShouldChange = true;
    
      @override
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
    
        return Scaffold(
            body: bDependenciesShouldChange ?
                Container(
                  height: 500,
                  alignment: Alignment.centerLeft,
                  child: C(child: B()),
                )
                : Container(
                  height: 500,
                  alignment: Alignment.centerLeft,
                  child: C(child: SizedBox(width: 20, height: 50, child: B())),
              ),
              floatingActionButton: FloatingActionButton(
                onPressed: _incrementCounter,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              )
        );
      }
    
      void _incrementCounter(){
        setState(() {
          bDependenciesShouldChange = !bDependenciesShouldChange;
        });
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        debugPrint("AdidChangeDependencies");
      }
    }
    
    class B extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => BState();
    }
    
    class BState extends State<B> {
      @override
      Widget build(BuildContext context) {
        return Center(child: Text("Test"));
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        debugPrint("BdidChangeDependencies");
      }
    }
    
    class C extends StatefulWidget {
      final Widget child;
    
      C({Key? key, required this.child}) : super(key: key);
    
      @override
      State<StatefulWidget> createState() => CState();
    }
    
    class CState extends State<C> {
      @override
      Widget build(BuildContext context) {
        return widget.child;
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        debugPrint("CdidChangeDependencies");
      }
    }
    
    class D extends StatelessWidget{
    
      final Widget child_D;
      D({required this.child_D});
    
      @override
      Widget build(BuildContext context) {
        return child_D;
      }
    }
    

    这个是网上流传的Demo,我把它调试为比较好可运行状态,大家可以来观察下输出,首先刚运行的时候就会打印如下:

    I/flutter (20121): AdidChangeDependencies
    I/flutter (20121): CdidChangeDependencies
    I/flutter (20121): BdidChangeDependencies
    

    看吧这里第一次就会打印这些 Satet 对应的 didChangeDependencies 函数呢,这是因为在一开始 Widget 对应的 Element 为空的情况下会运行如下代码

      Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
        if (newWidget == null) {
          if (child != null)
            deactivateChild(child);
          return null;
        }
        final Element newChild;
        if (child != null) {
          bool hasSameSuperclass = true;
          // When the type of a widget is changed between Stateful and Stateless via
          // hot reload, the element tree will end up in a partially invalid state.
          // That is, if the widget was a StatefulWidget and is now a StatelessWidget,
          // then the element tree currently contains a StatefulElement that is incorrectly
          // referencing a StatelessWidget (and likewise with StatelessElement).
          //
          // To avoid crashing due to type errors, we need to gently guide the invalid
          // element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition
          // returns false which prevents us from trying to update the existing element
          // incorrectly.
          //
          // For the case where the widget becomes Stateful, we also need to avoid
          // accessing `StatelessElement.widget` as the cast on the getter will
          // cause a type error to be thrown. Here we avoid that by short-circuiting
          // the `Widget.canUpdate` check once `hasSameSuperclass` is false.
          assert(() {
            final int oldElementClass = Element._debugConcreteSubtype(child);
            final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
            hasSameSuperclass = oldElementClass == newWidgetClass;
            return true;
          }());
          if (hasSameSuperclass && child.widget == newWidget) {
            if (child.slot != newSlot)
              updateSlotForChild(child, newSlot);
            newChild = child;
          } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
            if (child.slot != newSlot)
              updateSlotForChild(child, newSlot);
            child.update(newWidget);
            assert(child.widget == newWidget);
            assert(() {
              child.owner!._debugElementWasRebuilt(child);
              return true;
            }());
            newChild = child;
          } else {
            deactivateChild(child);
            assert(child._parent == null);
            newChild = inflateWidget(newWidget, newSlot);
          }
        } else {
          newChild = inflateWidget(newWidget, newSlot);
        }
    
        //省略以下代码
    }
    

    在运行到 updateChild 的时候 child 会为空(因为从没运行过),所以会运行到 newChild = inflateWidget(newWidget, newSlot); 这一句代码,如下:

     Element inflateWidget(Widget newWidget, Object? newSlot) {
        assert(newWidget != null);
        final Key? key = newWidget.key;
        if (key is GlobalKey) {
          final Element? newChild = _retakeInactiveElement(key, newWidget);
          if (newChild != null) {
            assert(newChild._parent == null);
            assert(() {
              _debugCheckForCycles(newChild);
              return true;
            }());
            newChild._activateWithParent(this, newSlot);
            final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
            assert(newChild == updatedChild);
            return updatedChild!;
          }
        }
        final Element newChild = newWidget.createElement();
        assert(() {
          _debugCheckForCycles(newChild);
          return true;
        }());
        newChild.mount(this, newSlot);
        assert(newChild._lifecycleState == _ElementLifecycle.active);
        return newChild;
      }
    

    其中会运行到 final Element newChild = newWidget.createElement(); 这一句用对应的Widget去创建对应的Element,随后调用 newChild.mount(this, newSlot);

    接下来到element.mount,他主要有两个运行分支流程:

    • 一个是ComponentElement的,
    • 一个是RenderObjectElement的,

    我们最常见的就是 ComponentElement.mount 代码如下:

    void mount(Element parent, dynamic newSlot) {
      super.mount(parent, newSlot);
      assert(_child == null);
      assert(_active);
      _firstBuild();
      assert(_child != null);
    }
    

    我可以给大家先说明一下 mount 这个函数其主要目的接着就把新建的子element挂载到了element树上,这个主要不是我们今天的内容所以不展开讲,想看细节的请大家关注我之前写的文章 <Flutter运行原理篇之Build构建的过程>

    RenderObjectElement.mount

    void mount(Element parent, dynamic newSlot) {
      super.mount(parent, newSlot);
    
      _renderObject = widget.createRenderObject(this);
    
      attachRenderObject(newSlot);
      _dirty = false;
    }
    

    此方法会创建一个RenderObject,之后就会把此RenderObject添加到RenderObject树上,想更清楚的话请大家关注我之前写的博客<Flutter运行原理篇之Build构建的过程>

    ComponentElement.mount 里面会运行一个 _firstBuild 方法:

    StatefulElement._firstBuild 如下:

      @override
      void _firstBuild() {
        assert(state._debugLifecycleState == _StateLifecycle.created);
        try {
          _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
          final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
          assert(() {
            if (debugCheckForReturnedFuture is Future) {
              throw FlutterError.fromParts(<DiagnosticsNode>[
                ErrorSummary('${state.runtimeType}.initState() returned a Future.'),
                ErrorDescription('State.initState() must be a void method without an `async` keyword.'),
                ErrorHint(
                  'Rather than awaiting on asynchronous work directly inside of initState, '
                  'call a separate method to do this work without awaiting it.',
                ),
              ]);
            }
            return true;
          }());
        } finally {
          _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
        }
        assert(() {
          state._debugLifecycleState = _StateLifecycle.initialized;
          return true;
        }());
        state.didChangeDependencies();
        assert(() {
          state._debugLifecycleState = _StateLifecycle.ready;
          return true;
        }());
        super._firstBuild();
      }
    

    ComponentElement._firstBuild

      void _firstBuild() {
        rebuild();
      }
    

    这里面会调用 state.didChangeDependencies(); 这个方法,这也就是我们重写的State的didChangeDependencies方法

    好了,我们搞清楚了第一次运行的时候 didChangeDependencies 方法的被调用的过程,现在我们来点击下一上面那个Demo的按钮,看看他又做了什么呢?

    我们点击了按钮调用了 setState 方法,这个方法会使build方法运行导致C的child被改变了,改为了SizedBox:

    body: bDependenciesShouldChange ?
                Container(
                  height: 500,
                  alignment: Alignment.centerLeft,
                  child: C(child: B()),
                )
                : Container(
                  height: 500,
                  alignment: Alignment.centerLeft,
                  child: C(child: SizedBox(width: 20, height: 50, child: B())),
              )
    

    熟悉Build运行流程的同学应该知道,setState 这个方法会触发到 updateChild 这个方法,上面有代码我就不贴了,这个方法里面会进行一个判断,判断 hasSameSuperclass 变量是否为false,如果为false则会触发 inflateWidget 进行Element的重构,也就是我们上面分析的步骤,我这里给大家贴一个图会比较明显:

    image.png

    大家看看,点击了按钮以后导致 hasSameSuperclass 变量判断为false(意思是他们不是同一个父亲),之后导致inflateWidget(SizeBox,newSlot) , 之后再会导致 SizeBox 的child为null,再会触发B进行inflateWidget方法重构其对应的Element

    逻辑部分都在 updateChild 函数里面:

    if (child != null) {
          bool hasSameSuperclass = true;
          //省略部分代码
    }else{
          newChild = inflateWidget(newWidget, newSlot); //这里面的newWidget是B
    }
    

    所以这样就会触发BState 对应的 didChangeDependencies 方法

    好了,到这里你应该就明白了为什么网上流传的结论 父级结构中的层级发生变化时didChangeDependencies被调用 这个结论的到底是怎么一回事了,其实这个会触发其对应的 Element重构 导致该方法被调用

    好了,既然我们已经搞清楚了调用的规则,我们不妨再来看看还会不会有别的情况会触发呢,我们再来看看 updateChild 方法里面的逻辑判断:

    Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
        if (newWidget == null) {
          if (child != null)
            deactivateChild(child);
          return null;
        }
        final Element newChild;
        if (child != null) {
          bool hasSameSuperclass = true;
          assert(() {
            final int oldElementClass = Element._debugConcreteSubtype(child);
            final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
            hasSameSuperclass = oldElementClass == newWidgetClass;
            return true;
          }());
          if (hasSameSuperclass && child.widget == newWidget) {
            if (child.slot != newSlot)
              updateSlotForChild(child, newSlot);
            newChild = child;
          } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
            if (child.slot != newSlot)
              updateSlotForChild(child, newSlot);
            child.update(newWidget);
            assert(child.widget == newWidget);
            assert(() {
              child.owner!._debugElementWasRebuilt(child);
              return true;
            }());
            newChild = child;
          } else {
            deactivateChild(child);
            assert(child._parent == null);
            newChild = inflateWidget(newWidget, newSlot);
          }
        } else {
          newChild = inflateWidget(newWidget, newSlot);
        }
    
        //省略以下代码
    }
    

    我们看到 (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) 这一行说明如果他们的父亲没有变化,但是 Widget.canUpdate 的返回发生了变化的话,那么他应该也会触发下面的 newChild = inflateWidget(newWidget, newSlot); 方法导致 didChangeDependencies 方法被调用,我们看看这个方法:

      static bool canUpdate(Widget oldWidget, Widget newWidget) {
        return oldWidget.runtimeType == newWidget.runtimeType
            && oldWidget.key == newWidget.key;
      }
    

    我们只需要改变他对应的key就可以实现我们的猜想了不是吗?

    所以我列出了下面的例子说明改情况:

    import 'package:flutter/material.dart';
    
    class TestDidChangeDependencies3 extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => TestDidChangeDependencies3State();
    }
    
    class TestDidChangeDependencies3State extends State<TestDidChangeDependencies3> {
      bool bDependenciesShouldChange = false;
      late Key k;
      @override
      void initState() {
        super.initState();
        k = Key('hello');
      }
    
      @override
      Widget build(BuildContext context) {
    
        return Scaffold(
            body: Container(
                  height: 500,
                  alignment: Alignment.centerLeft,
                  child: C(key:k,child: B()),
                ),
                  floatingActionButton: FloatingActionButton(
                    onPressed: _incrementCounter,
                    tooltip: 'Increment',
                    child: Icon(Icons.add),
                  )
        );
      }
    
      void _incrementCounter(){
        setState(() {
          k = Key('world');
        });
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        debugPrint("AdidChangeDependencies");
      }
    }
    
    class B extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => BState();
    }
    
    class BState extends State<B> {
      @override
      Widget build(BuildContext context) {
        return Center(child: Text("Test"));
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        debugPrint("BdidChangeDependencies");
      }
    }
    
    class C extends StatefulWidget {
      final Widget child;
    
      C({Key? key, required this.child}) : super(key: key);
    
      @override
      State<StatefulWidget> createState() => CState();
    }
    
    class CState extends State<C> {
      @override
      Widget build(BuildContext context) {
        return widget.child;
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        debugPrint("CdidChangeDependencies");
      }
    }
    

    例子很简单不详细讲解了,我们点击了按钮导致了C的key发生了变化导致C重构其Element也间接导致了B重构了对应的Element导致了如下的打印

    I/flutter (22761): AdidChangeDependencies
    I/flutter (22761): CdidChangeDependencies
    I/flutter (22761): BdidChangeDependencies
    I/flutter (22761): CdidChangeDependencies
    I/flutter (22761): BdidChangeDependencies
    

    好了,这里我们又学到了在不改变父类结构的情况下,如果改变Key也会导致didChangeDependencies被调用,但是其本质还是我一开始总结的:那就是在它的 State 对象对应的 Element 被 createElement() 的时候并且被Mount的时候就会被调用,简单就是说当这个对应的 Element 被重新创建的时候就会被调用

    好了看到这里你可能会松了一口气,总算明白了 didChangeDependencies 被调用背后的逻辑了,但是我要告诉你的是还没有完哦,😁这只是最普通的情况之一哦,还有一种更常见的情况那就是使用 InheritedWidget 的时候也会触发调用,这个是一个非常常用的控件,我们作为要深入flutter的开发者来说,不能只看简单的情况,这个控件也要必须搞清楚,那么在该文章的下半结我们来聊一聊 《InheritedWidget 以及对应的运行函数极其背后的逻辑》

    我们先来看看概念:InheritedWidget是 Flutter 中非常重要的一个功能型组件,它提供了一种在 widget 树中从上到下共享数据的方式,比如我们在应用的根 widget 中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget 中来获取该共享的数据!

    State对象有一个didChangeDependencies回调,它会在“依赖”发生变化时被Flutter 框架调用。而这个“依赖”指的就是子 widget 是否使用了父 widget 中InheritedWidget的数据!如果使用了,则代表子 widget 有依赖;如果没有使用则代表没有依赖。

    我们借用网上的Demo先举一个例子:

    import 'package:flutter/material.dart';
    
    class ShareDataWidget extends InheritedWidget {
      ShareDataWidget({
        Key? key,
        required this.data,
        required Widget child,
      }) : super(key: key, child: child);
    
      final int data; //需要在子树中共享的数据,保存点击次数
    
      //定义一个便捷方法,方便子树中的widget获取共享数据
      static ShareDataWidget? of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
      }
    
      //该回调决定当data发生变化时,是否通知子树中依赖data的Widget
      @override
      bool updateShouldNotify(ShareDataWidget old) {
        return old.data != data;
      }
    }
    
    class _TestWidget extends StatefulWidget {
      @override
      __TestWidgetState createState() => __TestWidgetState();
    }
    
    class __TestWidgetState extends State<_TestWidget> {
      @override
      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        return Text(ShareDataWidget.of(context)!.data.toString());
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("Dependencies change");
      }
    }
    
    class InheritedWidgetTestRoute extends StatefulWidget {
      @override
      _InheritedWidgetTestRouteState createState() => _InheritedWidgetTestRouteState();
    }
    
    class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
      int count = 0;
    
      @override
      Widget build(BuildContext context) {
        return  Center(
          child: ShareDataWidget( //使用ShareDataWidget
            data: count,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(bottom: 20.0),
                  child: _TestWidget(),//子widget中依赖ShareDataWidget
                ),
                ElevatedButton(
                  child: Text("Increment"),
                  //每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
                  onPressed: () => setState(() => ++count),
                )
              ],
            ),
          ),
        );
      }
    }
    
    image.png

    我们点击一次会加1,并且会打印一次 Dependencies change

    这种情况不像上面我们分析的改变结构的情况了,他是怎么回事呢?

    我们来看看点击了按钮 _TestWidget 是怎么拿到数据的,如下:

      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        return Text(ShareDataWidget.of(context)!.data.toString());
      }
    
      //定义一个便捷方法,方便子树中的widget获取共享数据
      static ShareDataWidget? of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
      }
    

    背后逻辑就在于 context.dependOnInheritedWidgetOfExactType<T>() 这句话:

    Element.dependOnInheritedWidgetOfExactType

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

    这里会从 _inheritedWidgets![T] 里面去取出对应的Element,那这个Element什么时候被添加进这个Map里面的呢,答案在于mount方法的调用逻辑:

    Element.mount

      @mustCallSuper
      void mount(Element? parent, Object? newSlot) {
       //省略部分代码
        _updateInheritance();
      }
    

    InheritedElement._updateInheritance

      @override
      void _updateInheritance() {
        assert(_lifecycleState == _ElementLifecycle.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; //注意这一句话
      }
    

    也就是说在InheritedElement被mount的时候会把自己添加进入这个Map _inheritedWidgets![widget.runtimeType] = this; ,所以在取的时候你可以拿到这个InheritedElement对象

    接下来再看看 dependOnInheritedElement 这个函数做了什么呢?

    Element.dependOnInheritedElement

      @override
      InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
        assert(ancestor != null);
        _dependencies ??= HashSet<InheritedElement>();
        _dependencies!.add(ancestor);
        ancestor.updateDependencies(this, aspect);
        return ancestor.widget;
      }
    
      @protected
      void updateDependencies(Element dependent, Object? aspect) {
        setDependencies(dependent, null);
      }
    
      @protected
      void setDependencies(Element dependent, Object? value) {
        _dependents[dependent] = value;
      }
    

    很简单不细说,就是把_TestWidget对应的Element添加进了_dependents 这个Map里面,准备作为触发的时候使用

    那什么时候触发呢,也就是在我们点击按钮以后会触发setState间接的触发InheritedElement的update方法更新的时候

    InheritedElement.updated

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

    ProxyElement.updated

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

    InheritedElement.notifyClients

      @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);  //注意这一句
        }
      }
    

    会取出dependent,也就是我们存的_TestWidget对应的Element,然后调用notifyDependent

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

    StatefulElement.didChangeDependencies

      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        _didChangeDependencies = true;
      }
    

    这里这一句很重要_didChangeDependencies = true; 这里的意思是把_TestWidget对应的Element的这个_didChangeDependencies属性变为true,等待_TestWidget对应的Element也就是StatefulElement对应的performRebuild方法调用的时候触发

    StatefulElement.performRebuild

      @override
      void performRebuild() {
        if (_didChangeDependencies) {
          state.didChangeDependencies();
          _didChangeDependencies = false;
        }
        super.performRebuild();
      }
    

    这里很明显了state.didChangeDependencies();这一句被调用了,说明我们重载的didChangeDependencies方法就会被调用了

    好了,到这里我们把 didChangeDependencies 方法什么时候被调用说得差不多了,但是大家还需要注意一个点就是 updateShouldNotify 这个函数的作用,这个函数网上说的是:是否通知子树中依赖的Widget调用其对应的 didChangeDependencies 方法,返回true则为通知,其实这里还有更深一层的含义在里面,就是这个返回值直接影响了依赖 InheritedWidget 对应对象的build方法是否执行

    最后再仔细看看,确实如果InheritedWidget对应的updateShouldNotify方法返回为false的话,那么super.updated(oldWidget);方法将无法调用

    InheritedElement.updated

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

    InheritedElement继承至ProxyElement

    ProxyElement.updated

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

    InheritedElement.notifyClients

      @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);
        }
      }
    }
    

    这里说得很清楚了,如果updateShouldNotify为false的话notifyDependent将不会被调用,那么State对应的didChangeDependencies也不会被调用

    这里还没有完,接着往下看,notifyDependent会调用到 didChangeDependencies方法:

    Element.didChangeDependencies

      void didChangeDependencies() {
        assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
        assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
        markNeedsBuild();
      }
    

    最终就不会到这里,也不会调用到依赖 InheritedWidget 对应对象的 markNeedsBuild 方法,所以他们的build方法无法被调用

    ProxyElement.build

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

    究其原因在于ProxyElement对应的build方法,他这个方法与其他的build方法不一样,其他的build方法如下 Widget build() => state.build(this); 都会调用重写的build方法,他这个build只是返回widget.child,并没有触发build方法调用所以导致了他的build不具备构建的能力,依赖 InheritedWidget 对应对象要想更新就不能使updateShouldNotify返回false

    这里是卡了我一个比较久的问题,当时确实想了半天才发现这里

    好了,这篇文章说到这里已经差不多了,相信能读玩的小伙伴一定是具有一定耐心的
    我也不在往下深究了,那不然结束不了了,总之今天我们讲了两个大致的内容:

    • didChangeDependencies方法调用背后的逻辑
    • InheritedWidget的基本使用

    希望能给大家带来帮助,喜欢我的文章的话欢迎给我点赞或者留言,我看到的话一定第一时间给你回复的谢谢大家,你的点赞加留言是我持续更新的动力,谢谢大家···

    相关文章

      网友评论

          本文标题:Flutter原理篇:didChangeDependencies

          本文链接:https://www.haomeiwen.com/subject/aokgfrtx.html