Flutter原理篇:didChangeDependencies

2021-12-19  本文已影响0人  耗子_wo

各位,又到了我们原理篇的时间了,基于最近几篇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.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

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

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

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

上一篇下一篇

猜你喜欢

热点阅读