之前几篇文章都提过 Flutter 的思想十分类似 React,通过 Widget Tree 来表示整个 app 的视图。一般来讲,常规 app 的开发中我们只需要与 和它的子类打交道,然而 Widget
并不是我们真实看到的视图,本文就来简单介绍一下 Flutter 视图系统背后的细节。
首先先摆 3 个关键的概念:Widget、Element、RenderObject。
Widget
为了解释 Widget
,我们先看看官方是如何定义它的:
Describes the configuration for an Element.
Widgets are the central class hierarchy in the Flutter framework. A widget is an immutable description of part of a user interface. Widgets can be inflated into elements, which manage the underlying render tree.
可以看到,Widget
的实际工作也就是描述如何创建 Element
,Widget
是一个不可变对象,它可以被复用,请注意,这里的复用不是指在两次渲染的时候将对象从旧树中拿过来放到新树,而是在同一个 Widget Tree 中,某个子 Widget
可以出现多次,因为它只是一个 description。
在一次渲染中,Flutter Framework 会调用 Widget
的 方法,这个方法会创建一个新的对应的 对象并返回。所以即使 Widget 被重复使用,框架还是会创建多个不同的 Element
对象。
到这里,Widget
的工作就暂告一段落了。
Element
Element
是 Widget
的实例体现,上面说过 Widget 可以重复使用,但是 Flutter Framework 仍然会对这几个相同的 Widget 依次创建几个全新的 Element
。 一次渲染会生成一个 Element Tree 并放在内存中,在下次渲染时 Flutter Framework 会用尝试用新的 Widget
去更新旧的 Element
,此时 Element
被复用,这里的复用是指不再创建新的 Element
的对象了,但每个相同的 Widget
还是各自对应了一个不同的 Element(好像有点绕,不知道大家能不能理解 -。-)。
那么 Element 到底是做什么的呢,概括地说就是保存了一个树形结构以便更新时做 diff、patch 和管理组件生命周期用的,由于 Widget
都是没有状态的,如果你想修改 Widget
的某一属性就必须要重新创建一遍 Widget
,而 StatefulWidget
内部包含 State
,如果重新创建了状态就会丢失,所以必须有一个地方来存储这些暂时的状态。
用 StatefulWidget
来举例说明吧。
第一次渲染时,StatefulWidget
通过 createElement
创建出一个 StatefulElement
,然后我们来看 StatefulElement
的构造方法:
StatefulElement(StatefulWidget widget)
: _state = widget.createState(), super(widget) {
assert(() {
if (!_state._debugTypesAreRight(widget)) {
throw new FlutterError(
'StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>\n'
'The createState function for ${widget.runtimeType} returned a state '
'of type ${_state.runtimeType}, which is not a subtype of '
'State<${widget.runtimeType}>, violating the contract for createState.'
);
}
return true;
}());
assert(_state._element == null);
_state._element = this;
assert(_state._widget == null);
_state._widget = widget;
assert(_state._debugLifecycleState == _StateLifecycle.created);
}
可以看到,StatefulElement
内部会负责创建和保存 State
,这样就是为什么 StatefulWidget
被重新创建了而内部的状态不会丢失的原因。
然后在 Widget Tree 发生变化的时候,Flutter Framework 通过 Element
的 update
来根据新 Widget
更新旧 Element
:
@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
final StatefulWidget oldWidget = _state._widget;
// Notice that we mark ourselves as dirty before calling didUpdateWidget to // let authors call setState from within didUpdateWidget without triggering // asserts. _dirty = true;
_state._widget = widget;
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
_state.didUpdateWidget(oldWidget);
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
rebuild();
}
所以如果你去看官方文档对 Element
的介绍,就会发现它就是负责状态和生命周期管理的对象,实际上很少需要去自己实现 Element
。
RenderObject
这个东西通过名字就能知道,它就是负责视图渲染的。实际上,所有的布局、绘制和事件响应全都由它负责,开发复杂视图时我们可能经常需要与之打交道。
而它又是由 Element
的子类 RenderObjectElement
创建出来的,RenderObject
也会构成一个 Render Tree,并且每个 RenderObject
也都会被保存下来以便在更新时复用。
通常我们也不直接 subclass RenderObject
本身,而是它的子类 RenderBox
,RenderBox
帮我们实现了很多基础设施,然而它并不能容纳子 RenderObject
,想要实现一个容器,我们需要使用 RenderObjectWithChildMixin<ChildType>
等 mixin,它们提供了对 children 的支持。
由于 RenderObject
本身又涉及很多细节,所以本文不再深入探讨。
以上就是 Flutter 视图系统对外大致的结构,希望对大家学习 Flutter 开发有所帮助~
References
1.
Widget class