美文网首页
Flutter中的Element(下篇)

Flutter中的Element(下篇)

作者: 沐灵洛 | 来源:发表于2020-08-05 21:10 被阅读0次

    上一篇我们简单介绍了Flutter中Element是什么以及它的生命周期。
    本篇我们将与大家一起探讨Element的一些重要的方法的作用以及调用时机。

    Element的重要方法

    • void mount(Element parent, dynamic newSlot)

    描述:parent element中给定的插槽上,加入当前的 element
    调用时机:新创建的element首次加入树中时,

    • Element updateChild(Element child, Widget newWidget, dynamic newSlot)

    描述:使用新的配置widget,更新树中element。这个方法是widges系统的核心方法。

    调用时机:每次基于更新后的widget,添加、更新或移除树中的child element时。

    方法原理:

    newWidget == null newWidget != null
    child == null Returns null. Returns new [Element].
    child != null Old child is removed, returns null. Old child updated if possible, returns child or new [Element].
    Element updateChild(Element child, Widget newWidget, dynamic newSlot) {                               
      if (newWidget == null) {                                                                                      
        if (child != null)     
          ///newWidget== null 弃用element                                                                                     
          deactivateChild(child);                                                                                   
        return null;                                                                                                
      }                                                                                                             
      Element newChild;                                                                                             
      if (child != null) {            
       ///标志位:解决hot reload前后Widget的类型不一致导致的类型错误  。
      ///比如:之前是`StatefulWidget`(StatefulElement) ,现在是`StatelessWidget`(StatelessElement)  。                                                          
        bool hasSameSuperclass = true;                                         
        assert(() {
         ///element is StatefulElement ? 1 :  element is StatelessElement ? 2 : 0;                                                                                                                                 
          final int oldElementClass = Element._debugConcreteSubtype(child);  
         /// widget is StatefulWidget ? 1 : widget is StatelessWidget ? 2 :  0;                                                                          
          final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);     
         ///确保类型一致                                  
          hasSameSuperclass = oldElementClass == newWidgetClass;                                                    
          return true;                                                                                              
        }());
        ///widget相等                                                                                                       
        if (hasSameSuperclass && child.widget == newWidget) { 
          ///只有一个child的`element`,它的`slot`为null                                                      
          if (child.slot != newSlot) 
          ///作用:修改给定的`child`在其`parent`中占据的slot。
         ///调用时机:`MultiChildRenderObjectElement`
         ///和其他拥有多个渲染对象的`RenderObjectElement`的子类。
         ///当它们的`child`从`element`的`child`(renderObject)数组
         ///的一个位置移动到其他位置时。                                                                     
          updateSlotForChild(child, newSlot);                                                                     
          newChild = child;                                                                                         
        } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {  
         ///询问Widget.canUpdate                              
          if (child.slot != newSlot)   
            ///修改给定的`child`在其`parent`中占据的slot。                                                                             
            updateSlotForChild(child, newSlot);
          ///使用新的配置,更新当前child element  
          child.update(newWidget);                                                                                  
          assert(child.widget == newWidget);                                                                        
          assert(() {                                                                                               
            child.owner._debugElementWasRebuilt(child);                                                             
            return true;                                                                                            
          }());                                                                                                     
          newChild = child;                                                                                         
        } else { 
          /// 弃用当前的child element                                                                                              
          deactivateChild(child);                                                                                   
          assert(child._parent == null);
         ///注入新的widget                                                                            
          newChild = inflateWidget(newWidget, newSlot);                                                             
        }                                                                                                           
      } else { 
         ///child == null   创建新的                                                                                            
        newChild = inflateWidget(newWidget, newSlot);                                                               
      }                                                                                                             
                                                                                                                    
      assert(() {                                                                                                   
        if (child != null)                                                                                          
          _debugRemoveGlobalKeyReservation(child);                                                                  
        final Key key = newWidget?.key;                                                                             
        if (key is GlobalKey) {                                                                                     
          key._debugReserveFor(this, newChild);                                                                     
        }                                                                                                           
        return true;                                                                                                
      }());                                                                                                         
                                                                                                                    
      return newChild;                                                                                              
    }                                                                                                               
    
    • void update(covariant Widget newWidget)

    描述:使用新widget修改用于配置此elementwidget

    调用时机:parent element希望使用不同的widget更新此element时,并且新的widget必须保证和旧的widget拥有相同的runtimeType

    • Element inflateWidget(Widget newWidget, dynamic newSlot)

    描述:为给定的widget创建一个element,并且将其作为child添加到当前element的给定slot中。

    调用时机:此方法通常由updateChild调用,但也可以由需要对创建的element进行更细粒度控制的子类直接调用。

    注意: 如果给定的widgetglobal key,并且其对应的element已经存在,这个函数将复用此element(可能从树中的另一个位置移植它,或从inactive元素列表中重新激活它)而不是创建一个新的元素。此方法返回的element已经被mounted并且处于active状态。

      Element inflateWidget(Widget newWidget, dynamic newSlot) {
        assert(newWidget != null);
        final Key key = newWidget.key;
       ///判断是否是`GlobalKey`
        if (key is GlobalKey) {
          ///从处于`inactive`的列表中,重新获取。
         ///1.final Element element = key._currentElement;
         ///2.element == null或!Widget.canUpdate(element.widget, newWidget)      
         ///reture null
         ///3. final Element parent = element._parent;
         ///4. parent.forgetChild(element);
         ///5. parent.deactivateChild(element);
         ///6.owner._inactiveElements.remove(element);
        ///7.return element 
          final Element newChild = _retakeInactiveElement(key, newWidget);
          if (newChild != null) {
            assert(newChild._parent == null);
            assert(() {
              _debugCheckForCycles(newChild);
              return true;
            }());
            ///重新激活获得的`element`。
            ///1.修改`parent`
            ///2._updateDepth(int parentDepth)
            ///3.递归遍历修改`element`中所有子元素的状态未`active`.
           ///针对拥有`children`的`element`。
           ///4.调用attachRenderObject(dynamic newSlot)
          ///添加渲染对象到树中。
            newChild._activateWithParent(this, newSlot);
           ///使用新的`widget`,更新此`element`,在新的`slot`上
            final Element updatedChild = updateChild(newChild, newWidget, newSlot);
            assert(newChild == updatedChild);
           ///返回updatedChild
            return updatedChild;
          }
        }
        ///如果不含globalKey
       ///创建新的元素
        final Element newChild = newWidget.createElement();
        assert(() {
          _debugCheckForCycles(newChild);
          return true;
        }());
       ///挂载:加入树中。
        newChild.mount(this, newSlot);
        assert(newChild._debugLifecycleState == _ElementLifecycle.active);
        return newChild;
      }
    
    • void deactivateChild(Element child)

    描述:inactive elements 列表中移除给定的element并且 从渲染树中分离此element所对应的render object

    调用时机:一般在元素更新(删除)其子元素时调用,但是在global key更换父节点时,新父会主动调用旧父的deactivateChild ,并且在此之前会先调用旧父的forgetChild,对旧父进行更新。

     void deactivateChild(Element child) {
        assert(child != null);
        assert(child._parent == this);
        ///去父
        child._parent = null;
        ///分离其渲染对象
        child.detachRenderObject();
        ///加入不活跃的数组中
        owner._inactiveElements.add(child); // this eventually calls child.deactivate()
        assert(() {
          if (debugPrintGlobalKeyedWidgetLifecycle) {
            if (child.widget.key is GlobalKey)
              debugPrint('Deactivated $child (keyed child of $this)');
          }
          return true;
        }());
      }
    
    • void detachRenderObject()

    描述:从渲染树中移除渲染对象。

    调用时机:一般在deactivateChild方法中被调用

    注意:该函数的默认实现只是对其child递归调用detachRenderObject,但是RenderObjectElement.detachRenderObject重写了此方法后,对其进行了覆盖(未调用super)。

      void detachRenderObject() {
        visitChildren((Element child) {
          child.detachRenderObject();
        });
        _slot = null;
      }
    
    • void forgetChild(Element child)

    描述:从元素的child列表中删除给定的child,以准备在元素树中其他位置重用该child

    调用时机:一般在deactivateChild方法调用前。update会负责创建或者更新用于替换此child的新的child
    关于对此方法的详细用法可以查看MultiChildRenderObjectElement类,此类中forgetChild会影响update时对于replaceWithNullIfForgotten方法的调用,也会影响visitChildren时对于子元素的遍历。

    • void updateSlotForChild(Element child, dynamic newSlot)

    描述:修改给定的child在父元素中占据的插槽 。
    调用时机:MultiChildRenderObjectElement和其他拥有多个渲染对象的RenderObjectElement的子类。当它们的childelementchild(renderObject)数组的一个位置移动到其他位置时。

     void updateSlotForChild(Element child, dynamic newSlot) {
        assert(_debugLifecycleState == _ElementLifecycle.active);
        assert(child != null);
        assert(child._parent == this);
        void visit(Element element) {
          element._updateSlot(newSlot);
          if (element is! RenderObjectElement)
            element.visitChildren(visit);
        }
        visit(child);
      }
    
    • void activate()

    描述:将元素的生命周期由inactive变为active
    调用时机:当之前停用的元素重新合并到树中时,框架将调用此方法。元素第一次激活时(即,状态从initial 开始时),framework不会调用此方法,而是通过调用mount

    void activate() {
        ....
        final bool hadDependencies = (_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies;
        _active = true;
        // We unregistered our dependencies in deactivate, but never cleared the list.
        // Since we're going to be reused, let's clear our list now.
        _dependencies?.clear();
        _hadUnsatisfiedDependencies = false;
        _updateInheritance();
        assert(() {
          _debugLifecycleState = _ElementLifecycle.active;
          return true;
        }());
        if (_dirty)
          ///将元素添加到`dirty element`列表中,
          ///以便在`WidgetsBinding.drawFrame`调用`buildScope`
         ///时将对其进行重建。
          owner.scheduleBuildFor(this);
        if (hadDependencies)
          didChangeDependencies();
      }
    
    • void attachRenderObject(dynamic newSlot)

    描述:renderObject添加到渲染树中slot指定的位置。
    调用时机:该函数的默认实现只是对其child递归调用attachRenderObject,但是RenderObjectElement.attachRenderObject重写了此方法后,对其进行了覆盖(未调用super)。

     void attachRenderObject(dynamic newSlot) {
        assert(_slot == null);
        visitChildren((Element child) {
          child.attachRenderObject(newSlot);
        });
        _slot = newSlot;
      }
    
    • void unmount()

    描述: 生命周期由inactive 变为 defunct
    调用时机:framework确定处于inactive状态的元素永远不会被激活时调用。在每个动画结束时,framework会对任何保持inactive状态的元素调unmount,以防止inactive元素保持inactive的时间超过单个动画帧。调用此函数后,该元素将不再合并到树中。

    • void reassemble()

    描述: 用于快速开发的一种debugging策略。
    调用时机: 在调试期间重新组装(reassemble)应用程序时调用,例如:hot reload期间。此函数仅在开发期间被调用,并且调用了reassemblebuild将至少被调用一次。

    • void didChangeDependencies()

    调用时机: 当此元素的依赖项更改时调用。比如 此元素依赖的InheritedElement更新了新的InheritedWidget并且InheritedWidget.updateShouldNotify返回true的时候,framework会调用此方法,通知此元素做出相应的改变。

    • void markNeedsBuild()

    描述: 将元素标记为dirty,并将其添加到全局的widget列表中,以在下一帧中进行重建。
    调用时机: setStatereassembledidChangeDependencies时。

    • void rebuild()

    调用时机:

    1. BuildOwner.scheduleBuildFor已经标记此元素为dirty时,由BuildOwner调用;
    2. 当元素通过mount首次被构建时会调用;
    3. 当元素的widget通过update已经被改变时,会调用此方法。

    相关文章

      网友评论

          本文标题:Flutter中的Element(下篇)

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