继上一篇 Flutter学习笔记(1)介绍了一下Flutter框架构造,本篇主讲Flutter的视图体系,下一篇开始布局
引言
- Flutter官方文档里的一句话:you build your UI out of widgets(万物皆控件)
- 首先,Flutter没有css,没有xml,Dart语言是面向对象的
- 其次Widget并不是我们真正看到的视图,背后究竟是什么?
- Flutter界面开发是一种响应式编程,而Widget又是一层不变的,那真正的渲染、布局、刷新是谁来处理?
Flutter的视图体系
Flutter Framework提供了三种视图树,即:Widget
Element
RenderObject
- 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 的工作就暂告一段落了。
Widget这个类中都包含哪些属性:
- KEY key
- int hasCode
- TYPE runtimeType
Widget是用户界面的一部分,并且是不可变的(immutable)。Widget会被inflate到Element,并由Element管理底层渲染树。Widget本身没有可变状态(所有的字段必须是final)。如果想要把可变状态与Widget关联起来,可以使用StatefulWidget,StatefulWidget通过使用StatefulWidget.createState方法创建State对象,并将之扩充到Element以及合并到树中;
- Element
-
负责状态和生命周期管理的对象,实际上很少需要去自己实现 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();
}
- RenderObject
官网定义:An object in the render tree.渲染树中的一个对象。从其名字,我们可以很直观地知道,它就是负责渲染的工作;
RenderObject的属性太多,且该类的细节涉及很多渲染知识,我们会在后面的系列中再详细说明其工作原理。
总结
-
Widget:存放渲染内容、视图布局信息,widget的属性最好都是immutable
-
Element:存放上下文,通过Element遍历视图树,Element同时持有Widget和RenderObject
-
RenderObject:根据Widget的布局属性进行layout,paint Widget传人的内容
网友评论