写点什么

【Flutter 专题】101 何为 Flutter Elements ?

发布于: 2 小时前
【Flutter 专题】101 何为 Flutter Elements ?

      小菜前段时间简单了解了一下 Widget 的相关知识,其中 Widgetimmutable 不可变的,而 Widget 是如何做到更新重绘的,这就离不开 ElementRenderObject;小菜简单了解一下 Element 的相关小知识;



Element

      ElementWidgetUI 树具体位置的一个实例化对象;UI ViewElement 层级结构中会构建一个真实的 Element Tree,是真正的视图树结构;Element 作为 WidgetRenderObject 之间的协调者,并根据 Widget 的变化来完成结点的增删改的操作;



源码分析

      Element 所涉及源码较长,小菜仅针对具体的方法和生命周期进行学习;

生命周期

enum _ElementLifecycle {  initial,  active,  inactive,  defunct,}
复制代码


      Element 的生命周期主要包括如下几分,分别是 initial 初始化,active 活跃状态,inactive 不活跃状态以及 defunct 失效状态;

针对方法

1. createElement
Element(Widget widget) : assert(widget != null), _widget = widget;
复制代码


      创建一个使用指定 Widget 作为其配置的 Element;通过 Widget 调用 Widget.createElement 来创建 Element,作为 Element 的初始位置;

2. mount
@mustCallSupervoid mount(Element parent, dynamic newSlot) {    ...    _parent = parent;    _slot = newSlot;    _depth = _parent != null ? _parent.depth + 1 : 1;    _active = true;    if (parent != null)       _owner = parent.owner;    if (widget.key is GlobalKey) {      final GlobalKey key = widget.key;      key._register(this);    }    _updateInheritance();}
复制代码


      mount() 会将新创建的 Element 添加到指定的父级 slot 插槽树中,通过调用 attachRenderObject 添加到渲染树上;

3. update
@protectedElement updateChild(Element child, Widget newWidget, dynamic newSlot) {    if (newWidget == null) {      if (child != null) deactivateChild(child);      return null;    }    if (child != null) {      if (child.widget == newWidget) {        if (child.slot != newSlot) updateSlotForChild(child, newSlot);        return child;      }      if (Widget.canUpdate(child.widget, newWidget)) {        if (child.slot != newSlot) updateSlotForChild(child, newSlot);        child.update(newWidget);        return child;      }      deactivateChild(child);      assert(child._parent == null);    }    return inflateWidget(newWidget, newSlot);}
复制代码


      updateChildElement 的核心方法,每当需要增加,修改,删除子 child 时都会调用;主要根据 Widget 的变化用于 Element 的更新,进而更新 UI 树;


  1. 当更新后的 Widgetnull 时,对应的子节点已经移除,如果当前 child 不为 null,则直接 remove 掉;

  2. 当更新后的 Widget 不为 null 且当前 childnull 时,说明新 Widget 是新创建的,则 inflateWidget 创建子节点;

  3. 当更新后的 Widget 不为 null 且当前 child 也不为 null 该节点存在时,若 child.widget == newWidget 说明子节点前后未发生变化,若 child.slot != newSlot 说明子节点在兄弟结点间移动了位置,此时 updateSlotForChild 更新节点位置;否则直接返回子节点;

  4. 当更新后的 Widget 不为 null 且当前 child 也不为 null 该节点存在时,若 Widget.canUpdatetrue 说明可以用 newWidget 修改子节点,直接调用 update 更新即可;否则先将子节点移除再通过 newWidget 创建新的子节点;其中 canUpdate 主要是判断新旧 WidgetkeyruntimeType 是否一致;

4. deactivate
@protectedvoid deactivateChild(Element child) {    child._parent = null;    child.detachRenderObject();    owner._inactiveElements.add(child); // this eventually calls child.deactivate()}
复制代码


      deactivateChild 将指定 Element 已到非活动 Element 列表中,并将渲染对象从渲染树中移除;该方法可以阻止 Element 成为其子类;

5. activate
@mustCallSupervoid activate() {    final bool hadDependencies = (_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies;    _active = true;    _dependencies?.clear();    _hadUnsatisfiedDependencies = false;    _updateInheritance();        if (_dirty)      owner.scheduleBuildFor(this);    if (hadDependencies)      didChangeDependencies();}
复制代码


      activate() 为将 Element 重新合并到树上时,框架会从 inactive 非活跃 Element 列表中删除该元素,且该元素调用 activate 并将 Element 的渲染对象添加到渲染树上;

6. unmount
@mustCallSupervoid unmount() {    if (widget.key is GlobalKey) {        final GlobalKey key = widget.key;        key._unregister(this);    }}
复制代码


      unmount() 为当框架永远不会重新激活时调用;为了避免在一次动画执行过程中反复创建,移除特定 Element 时,非活跃状态的 Element 都会在当前动画过程最后一帧先保留,如果到动画结束后还未变成活跃状态,则调用 unmount() 将该 Element 彻底移除;

对应关系

  1. Widget.createElementinitial 从无到有的初始化生命周期;

  2. mountinitial 初始化状态到 active 活跃状态到生命周期过渡;

  3. update 只有在 active 活跃状态时才会调用;

  4. deactivateactive 活跃状态到 inactive 非活跃状态生命周期过渡;

  5. activateinactive 非活跃状态到 active 活跃状态的生命周期过渡;

  6. unmountinactive 非活动状态到 defunct 失效状态生命周期的过渡;

子类 Element

      Element 主要有组合类 ComponentElement 和渲染类 RenderObjectElement 两个子类;

ComponentElement

      ComponentElement 为组合类 Element,主要包括如下 StatelessElement / StatefulElement / ProxyElement 子类;其中各 Element 都是与 Widget 对应的;


StatelessElement


class StatelessElement extends ComponentElement {  StatelessElement(StatelessWidget widget) : super(widget);
@override StatelessWidget get widget => super.widget;
@override Widget build() => widget.build(this);
@override void update(StatelessWidget newWidget) { super.update(newWidget); assert(widget == newWidget); _dirty = true; rebuild(); }}
复制代码


      StatelessElement 相对比较简单,主要是重写 update() 在需要发生变更时 rebuild() 即可;


StatefulElement


@overridevoid update(StatefulWidget newWidget) {    super.update(newWidget);    final StatefulWidget oldWidget = _state._widget;    _dirty = true;    _state._widget = widget;    try {      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);      final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;    } finally {      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);    }    rebuild();}
复制代码


      StatefulElement 是对应 StatefulWidget 的,包括完整的 Element 生命周期,其中在更新时会更新 Staterebuild()


ProxyElement


abstract class ProxyElement extends ComponentElement   ProxyElement(ProxyWidget widget) : super(widget);
@override ProxyWidget get widget => super.widget;
@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); }
@protected void notifyClients(covariant ProxyWidget oldWidget);}
复制代码


      ProxyElement 作为一个抽象类,其子类是 ParentDataElementInheritedElement;当 Widget 更新时调用 update()notifyClients() 用于新旧 Widget 确实已改变时调用;

RenderObjectElement

      RenderObjectElement 为渲染类型 Element 对应的是 RenderObjectWidgetRenderObjectElement 作为抽象类也继承了 Element 所有的生命周期方法;


      大多数的 RenderObjectElement 都只对应一个 RenderObject 即只有一个子节点,例如 RootRenderObjectElement / SingleChildRenderObjectElement;但也有特殊的,如 LeafRenderObjectElement 子类没有子节点,以及 MultiChildRenderObjectElement 子类可以有多个子节;




      Element 作为 WidgetRenderObject 的协作者起到了承上启下的左右;小菜对会在下一篇简单学习 RenderObject;小菜对源码的理解还不够深入,如有错误,请多多指导!


来源:阿策小和尚

发布于: 2 小时前阅读数: 3
用户头像

还未添加个人签名 2021.05.13 加入

Android / Flutter 小菜鸟~

评论

发布
暂无评论
【Flutter 专题】101 何为 Flutter Elements ?