Flutter树的渲染机制和原理
Flutter树的三大对象:Widget、Element、RenderObject
Widget
Widget定义
在Flutter中,一切皆是组件。我们使用Flutter开发时就用了各种的Widget,但是实际渲染页面的并不是Widget,Flutter中对Widget的定义:
Describes the configuration for an [Element].
翻译过来就是 ”对一个[Element]配置的描述”
由此可见,Widget只是一个配置描述,并不是真正的渲染对象。那么什么是Element呢,简单分析一下Widget的源码:
@immutable
abstract class Widget extends DiagnosticableTree {
/// Initializes [key] for subclasses.
const Widget({ this.key });
final Key? key;
@protected
@factory
Element createElement();
Widget有一个构造,参数是Key,这里key的作用就是比较两个Widget是否是同一个Widget,如果两个Widget的runtimeType和key都相等,则复用Element进行更新,替换掉旧的Widget,否则,Element就会被从树中移除,新的Widget被扩充到新的Element中,然后再把这个新的Element挂载树中。
还有一个方法是createElement(),子类必须实现这个方法,大部分我们使用的系统的Widget比如StatefulWidget和StatelessWidget,都默认实现了这个方法,这个方法也非常简单,就是创建了一个Element。现在我们可以知道Widget与Element的关系:一个Widget有一个Element对象,通过createElement()创建。
Widget特性
Widget类被标注了@immutable,意味不可变的,想要改变Widget就得创建一个新的。
Element
Element定义
Flutter对Element的定义:
An instantiation of a [Widget] at a particular location in the tree.
翻译之后就是 ”一个关联Widget并在Widget树特定位置的实例“
简单分析一下Element源码:
abstract class Element extends DiagnosticableTree implements BuildContext {
/// Creates an element that uses the given widget as its configuration.
///
/// Typically called by an override of [Widget.createElement].
Element(Widget widget)
: assert(widget != null),
_widget = widget;
Element? _parent;
@protected
@pragma('vm:prefer-inline')
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
……
}
@mustCallSuper
void mount(Element? parent, Object? newSlot) {
……
}
mount方法
当一个Widget被创建时就会相应的创建关联的Element,framework就会调用mount方法来把这个Element插入到Element树中。
看一个最简单不过的Opacity类,这个Widget只有一个child和opacity属性,作用是控制子类的透明度。Opacity继承了SingleChildRenderObjectWidget,该类会创建一个Element:SingleChildRenderObjectElement
class SingleChildRenderObjectElement extends RenderObjectElement {
/// Creates an element that uses the given widget as its configuration.
SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_child = updateChild(_child, widget.child, null);
}
这边调用了updateChild方法和父类的mount方法,看一下父类的mount,这边去除不必要的代码
abstract class RenderObjectElement extends Element {
/// Creates an element that uses the given widget as its configuration.
RenderObjectElement(RenderObjectWidget widget) : super(widget)
/// The underlying [RenderObject] for this element.
@override
RenderObject get renderObject => _renderObject!;
RenderObject? _renderObject;
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
可以看到这里通过widget创建了一个RenderObject。
Element分别持有了Widget对象和RenderObject对象,到这我们可以明白一个Element包含了一个Widget和一个RenderObject,Element和RenderObject是由Widget生成的。
其中还有一个attachRenderObject方法:
@override
void attachRenderObject(Object? newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
final ParentDataElement<ParentData>? parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
这个方法主要功能就是将RenderObject关联到RenderObject树上,这时可以认为Widget已经渲染在屏幕上了。
RenderObject
RenderObject定义
通过以上的说明,已经清楚Flutter中真正渲染页面的对象就是RenderObject
Flutter对RenderObject的定义:
An object in the render tree.
翻译过来就是 ”渲染树上的一个对象“
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
/// Initializes internal fields for subclasses.
RenderObject() {
_needsCompositing = isRepaintBoundary || alwaysNeedsCompositing;
}
void layout(Constraints constraints, { bool parentUsesSize = false }) {
……
}
void paint(PaintingContext context, Offset offset) { }
void performLayout();
void markNeedsPaint() {
……
}
……
这些方法可以看出来RenderObject的主要职责就是布局和绘制。