【Flutter 专题】101 何为 Flutter Elements ?#yyds干货盘点#
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.
【Flutter 专题】101 何为 Flutter Elements ?#yyds干货盘点#
推荐 原创 小菜前段时间简单了解了一下 Widget 的相关知识,其中 Widget 是 immutable 不可变的,而 Widget 是如何做到更新重绘的,这就离不开 Element 和 RenderObject;小菜简单了解一下 Element 的相关小知识;
Element
Element 是 Widget 在 UI 树具体位置的一个实例化对象;UI View 在 Element 层级结构中会构建一个真实的 Element Tree,是真正的视图树结构;Element 作为 Widget 和 RenderObject 之间的协调者,并根据 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
@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);
}
updateChild 是 Element 的核心方法,每当需要增加,修改,删除子 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]. |
- 当更新后的 Widget 为 null 时,对应的子节点已经移除,如果当前 child 不为 null,则直接 remove 掉;
- 当更新后的 Widget 不为 null 且当前 child 为 null 时,说明新 Widget 是新创建的,则 inflateWidget 创建子节点;
- 当更新后的 Widget 不为 null 且当前 child 也不为 null 该节点存在时,若 child.widget == newWidget 说明子节点前后未发生变化,若 child.slot != newSlot 说明子节点在兄弟结点间移动了位置,此时 updateSlotForChild 更新节点位置;否则直接返回子节点;
- 当更新后的 Widget 不为 null 且当前 child 也不为 null 该节点存在时,若 Widget.canUpdate 为 true 说明可以用 newWidget 修改子节点,直接调用 update 更新即可;否则先将子节点移除再通过 newWidget 创建新的子节点;其中 canUpdate 主要是判断新旧 Widget 的 key 和 runtimeType 是否一致;
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 彻底移除;
- Widget.createElement 为 initial 从无到有的初始化生命周期;
- mount 为 initial 初始化状态到 active 活跃状态到生命周期过渡;
- update 只有在 active 活跃状态时才会调用;
- deactivate 为 active 活跃状态到 inactive 非活跃状态生命周期过渡;
- activate 为 inactive 非活跃状态到 active 活跃状态的生命周期过渡;
- unmount 为 inactive 非活动状态到 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 生命周期,其中在更新时会更新 State 和 rebuild();
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 作为一个抽象类,其子类是 ParentDataElement 和 InheritedElement;当 Widget 更新时调用 update();notifyClients() 用于新旧 Widget 确实已改变时调用;
RenderObjectElement
RenderObjectElement 为渲染类型 Element 对应的是 RenderObjectWidget;RenderObjectElement 作为抽象类也继承了 Element 所有的生命周期方法;
大多数的 RenderObjectElement 都只对应一个 RenderObject 即只有一个子节点,例如 RootRenderObjectElement / SingleChildRenderObjectElement;但也有特殊的,如 LeafRenderObjectElement 子类没有子节点,以及 MultiChildRenderObjectElement 子类可以有多个子节;
Element 作为 Widget 和 RenderObject 的协作者起到了承上启下的左右;小菜对会在下一篇简单学习 RenderObject;小菜对源码的理解还不够深入,如有错误,请多多指导!
来源:阿策小和尚
- 赞
- 收藏
- 评论
- 分享
- 举报
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK