【Flutter 专题】101 何为 Flutter Elements ?
和尚前段时间简单了解了一下 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;和尚对源码的理解还不够深入,如有错误,请多多指导!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 并发编程之Atomic&Unsafe魔法类详解
- Hexo+Next7.X 博客美化教程合集
- 波士顿房价预测 - 最简单入门机器学习 - Jupyter
- 机器学习竞赛分享:NFL大数据碗(上篇)
- 大文件分割、命名脚本 - Python
- adb常用命令总结
- Cypress执行测试并邮寄发送测试报告
- 重拾前端技能为你的职业前程保驾护航
- 检查图片是否损坏、图片后缀是否与实际图片类型对应 - Python
- 规则引擎 - drools 使用讲解(简单版) - Java
- Python奇技淫巧 - 持续更新中....
- 150+行Python代码实现带界面的数独游戏
- Python实现十大经典排序算法
- 140行Python代码实现Flippy Bird
- 200行Python代码实现贪吃蛇