Flutter 中的 Widget, Element, RenderObject(二)
class Padding extends SingleChildRenderObjectWidget {
// ...
@override
RenderPadding createRenderObject(BuildContext context) {
return RenderPadding(
// ...
);
}
}
class RichText extends MultiChildRenderObjectWidget {
// ...
@override
RenderParagraph createRenderObject(BuildContext context) {
return RenderParagraph(
// ...
);
}
// ...
}
Widget
Flutter
中一切皆 Widget
,我们先给 Widget
分一下类。
- 最常用的两个:
StatelessWidget
,StatefulWidget
,它们没有自己的RenderObject
,用于组合其他Widget
- 具有 RenderObject:
RichText
,Padding
,Opacity
等 - Flutter 预设的:
Text
,Container
等,使用非常频繁,Flutter
预先封装好的StatelessWidget
- Flutter 框架层面的:
RenderObjectWidget
,Leaf/SingleChild/MultiChildRenderObjectWidget
他们的继承关系如下图。
我们看 Widget
接口的源码。
@immutable
abstract class Widget extends DiagnosticableTree {
/// Initializes [key] for subclasses.
const Widget({ this.key });
final Key? key;
@protected
@factory
Element createElement();
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
Widget
是个抽象类,其声明的方法 createElement
需要子类是去实现,这就体现了我们前面说的通过 widget
树创建 Element
树,这两棵树是一一对应的关系。显然,在 StatelessWidget
, StatefulWidget
, Leaf/SingleChild/MultiChildRenderObjectWidget
里面可以找到 createElement
的实现。
abstract class StatelessWidget extends Widget {
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
abstract class StatefulWidget extends Widget {
@override
StatefulElement createElement() => StatefulElement(this);
@protected
@factory
State createState();
}
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
@override
LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
@override
SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
}
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
@override
MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
}
可以发现,在创建 Element
时都会传入 this
进去,也就是当前的 widget
,然后再返回对应的 Element
,这些 Element
都继承自 Element
类,Elment
拥有当前 widget
的引用。
我们之前说过:
Element
挂载时创建RenderObject
而 RenderObject
的创建与更新的方法其实是在具体的 widget
中声明的。
class Padding extends SingleChildRenderObjectWidget {
// ...
@override
RenderPadding createRenderObject(BuildContext context) {
return RenderPadding(
// ...
);
}
@override
void updateRenderObject(BuildContext context, RenderPadding renderObject) {
renderObject
..padding = padding
..textDirection = Directionality.maybeOf(context);
}
}
所以,Element
需要拿到 widget
的引用,这样 Element
在创建和更新 RenderObject
时,去调用 widget
中声明的这两个方法。
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
// ...
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
// ...
attachRenderObject(newSlot);
super.performRebuild(); // clears the "dirty" flag
}
Flutter
这么设计的原因是,我们平时写的 widget
只是配置信息,它需要的是 RenderObject
(而不是Element
)便于将配置信息直接传入进行渲染。 这就弄清楚了文章开头说的好像是 widget
创建的 RenderObject
的疑惑。
Element
上面源码中,我们介绍了 Widget
都会生成对应的 Element
树,与 Widget
一样,我们也对 Element
做一下分类。
我们来看看 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)
: _widget = widget {
// ...
}
// ...
}
前面说到 createElement
方法出入的 this
:
@override
SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
就是一级一级透传至 Element
构造方法中的。
class SingleChildRenderObjectElement extends RenderObjectElement {
/// Creates an element that uses the given widget as its configuration.
SingleChildRenderObjectElement(SingleChildRenderObjectWidget super .widget);
}
abstract class RenderObjectElement extends Element {
/// Creates an element that uses the given widget as its configuration.
RenderObjectElement(RenderObjectWidget super .widget);
}
this
最终赋值给了 Element
的 _widget
变量。
@override
Widget get widget => _widget!;
Widget? _widget;
从源码中可以看到,Element
里面的 widget
是个 get
方法,直接返回 _widget
。从源码中的注释,也再次看到 Widget
和 Element
的关系,Widget
是 Element
的配置。
对于 Element
的构造方法,StatefulElement
有些特殊的地方。
class StatelessElement extends ComponentElement {
/// Creates an element that uses the given widget as its configuration.
StatelessElement(StatelessWidget super.widget);
}
class StatefulElement extends ComponentElement {
/// Creates an element that uses the given widget as its configuration.
StatefulElement(StatefulWidget widget)
: _state = widget.createState() ,
super(widget) {
// ...
state._element = this;
// ...
state._widget = widget;
}
/// The [State] instance associated with this location in the tree.
///
/// There is a one-to-one relationship between [State] objects and the
/// [StatefulElement] objects that hold them. The [State] objects are created
/// by [StatefulElement] in [mount].
State<StatefulWidget> get state => _state!;
State<StatefulWidget>? _state;
}
StatefulElement
的构造方法还调用了对应 widget
的 createState
方法,并将其赋值给 _state
变量。我们平时写的 StatefulWidget
中的 createState
方法就是在这个时机调用的。
StatefulElement
不仅有对 widget
的引用,也有对 StatefulWidget
的 State
的引用。在构造函数中也将传进去 widget
赋值给 _state
的 _widget
属性。所以我们在 State
中可以使用 widget.
的方式拿到 widget
中的值。
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
前面文章中有说到渲染过程:
- 首先,通过
Widget
树生成对应的Element
树;- 然后,
Element
挂载时创建RenderObject
,并关联至Element.renderObject
属性上;- 最后,
RenderObject
树完成最终的渲染。
createElement
和 mount
都是 Flutter
框架自动调用的,开发者不必关注这些过程。基类 Element
里面的 mount
方法需要子类来实现。我们先来看看 ComponentElement
中的 mount
方法。
// ComponentElement
@override
void mount(Element? parent, Object? newSlot) {
// ...
_firstBuild();
}
经过深挖发现,这里执行的顺序如下:
我们来看看 StatelessElement/StatefulElement
中的 build
方法。
// StatelessElement
@override
Widget build() => (widget as StatelessWidget).build(this);
// StatefulElement
@override
Widget build() => state.build(this);
对比很明显,StatelessElement
执行的是 widget
中的 build
方法,而 StatefulElement
执行的是 State
的 build
方法。二者的 build
中都是传入的 this
,也就是当前的 Element
,我们在写代码时,build
方法传入的都是 context
。
@override
Widget build(BuildContext context) {}
所以 BuildContext
其实就是当前 widget
所对应的 Element
。事实上,Element
就是实现了 BuildContext
。
abstract class Element extends DiagnosticableTree implements BuildContext {}
我们再来看看 RenderObjectElement
中的 mount
方法。
// RenderObjectElement
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
// ...
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
// ...
attachRenderObject(newSlot);
super.performRebuild(); // clears the "dirty" flag
}
对比 ComponentElement
(StatelessElement
, StatefulElement
)里面的 mount
方法,RenderObjectElement
的 mount
方法是调用 widget
的 createRenderObject
方法生成对应的 RenderObject
,然后赋值给 _renderObject
。
说到这里,我们再来回顾一下前面对 Widget 的分类。
- 最常用的两个:
StatelessWidget
,StatefulWidget
,它们没有自己的RenderObject
,用于组合其他Widget
- 具有 RenderObject:
RichText
,Padding
,Opacity
等- Flutter 预设的:
Text
,Container
等,使用非常频繁,Flutter
预先封装的StatelessWidget
- Flutter 框架层面的:
RenderObjectWidget
,Leaf/SingleChild/MultiChildRenderObjectWidget
StatelessWidget
, StatefulWidget
没有自己��� RenderObject
,他们只是用来组合其他 widget
的。Flutter
中的页面搭建就像是搭积木,通过不同的组合可以搭建出不一样的页面。而像 Padding
, Opacity
等就是积木,这些积木都有自己的 RenderObject
。
总结来说,ComponentElement
中的 mount
方法是执行 widget
的 build
方法,就是我们自己写的代码(继承自 StatelessWidget
和 StatefulWidget
),而 RenderObjectElement
中的 mount
方法作用是调用“积木”的 createObject
方法生成对应的 RenderObject
,用于页面渲染。
RenderObject
关于 RenderObject
,其实前面都有交叉介绍到了,这里再做些补充和总结。
- 负责实现视图渲染的对象,是个总称,不同的
widget
会有不同类型的RenderObject
- 并不是所有的
widget
都有RenderObject
,只有继承自RenderObjectWidget
的Widget
才有对应的RenderObject
RenderObject
的创建与更新的方法是在具体的widget
里面定义的- 在
RenderObjectElement
执行mount
方法的时候调用的widget
里面的createRenderObject
方法的 RenderObjectElement
里面既有对widget
的引用也有对RenderObject
的引用。
RenderObject
在 Flutter
的展示过程分为四个阶段,即布局、绘制、合成和渲染。 其中,布局和绘制在 RenderObject 中完成,Flutter 采用深度优先机制遍历 RenderObject
树,确定树中各个对象的位置和尺寸,并把它们绘制到不同的图层上。绘制完毕后,合成和渲染的工作则交给 Skia
来搞定。在 VSync
信号同步时直接从渲染树合成 Bitmap
,然后提交给 GPU
。
转载自:https://juejin.cn/post/7393189744330276902