1

【Flutter 专题】101 何为 Flutter Elements ?#yyds干货盘点#

 2 years ago
source link: https://blog.51cto.com/xace/5260632
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【Flutter 专题】101 何为 Flutter Elements ?#yyds干货盘点#

推荐 原创

      小菜前段时间简单了解了一下 Widget 的相关知识,其中 Widgetimmutable 不可变的,而 Widget 是如何做到更新重绘的,这就离不开 ElementRenderObject;小菜简单了解一下 Element 的相关小知识;
【Flutter 专题】101 何为 Flutter Elements ?#yyds干货盘点#_Android 小菜鸟

Element

      ElementWidgetUI 树具体位置的一个实例化对象;UI ViewElement 层级结构中会构建一个真实的 Element Tree,是真正的视图树结构;Element 作为 WidgetRenderObject 之间的协调者,并根据 Widget 的变化来完成结点的增删改的操作;
【Flutter 专题】101 何为 Flutter Elements ?#yyds干货盘点#_0 基础学习 Flutter_02

      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
@mustCallSuper
void 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
@protected
Element 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 树;

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].
  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
@protected
void deactivateChild(Element child) {
    child._parent = null;
    child.detachRenderObject();
    owner._inactiveElements.add(child); // this eventually calls child.deactivate()
}

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

5. activate
@mustCallSuper
void 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
@mustCallSuper
void 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
@override
void 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;小菜对源码的理解还不够深入,如有错误,请多多指导!

来源:阿策小和尚

  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK