1 Widget
- Widget就是一个个描述文件,这些描述文件在我们进行状态改变时会不断的build。
- 但是对于渲染对象来说,只会使用最小的开销来更新渲染界面。
Padding是一个Widget,并且继承自SingleChildRenderObjectWidget
继承关系如下:
Padding -> SingleChildRenderObjectWidget -> RenderObjectWidget -> Widget
我们之前在创建Widget时,经常使用StatelessWidget和StatefulWidget,这种Widget只是将其他的Widget在build方法中组装起来,并不是一个真正可以渲染的Widget。
在Padding的类中,我们找不到任何和渲染相关的代码,这是因为Padding仅仅作为一个配置信息,这个配置信息会随着我们设置的属性不同,频繁的销毁和创建。
2 RenderObject
- 渲染树上的一个对象
- RenderObject层是渲染库的核心, 真正负责渲染widget。
- 其中有markNeedsLayout, performLayout, markNeedsPaint paint等方法
Padding里面的代码,有一个非常重要的方法createRenderObject:
- 这个方法其实是来自RenderObjectWidget的类,在这个类中它是一个抽象方法;
- 抽象方法是必须被子类实现的,但是它的子类SingleChildRenderObjectWidget也是一个抽象类,所以可以不实现父类的抽象方法
- 但是Padding不是一个抽象类,必须在这里实现对应的抽象方法,而它的实现就是下面的实现
RenderPadding的继承关系是什么呢?@override
RenderPadding createRenderObject(BuildContext context) {
return RenderPadding(
padding: padding,
textDirection: Directionality.of(context),
);
}
RenderPadding -> RenderShiftedBox -> RenderBox -> RenderObject
我们来具体查看一下RenderPadding的源代码:
- 如果传入的_padding和原来保存的value一样,那么直接return;
- 如果不一致,调用_markNeedResolution,而_markNeedResolution内部调用了markNeedsLayout;
- 而markNeedsLayout的目的就是标记在下一帧绘制时,需要重新布局performLayout;
如果我们找的是Opacity,那么RenderOpacity是调用markNeedsPaint,RenderOpacity中是有一个paint方法的;
set padding(EdgeInsetsGeometry value) {
assert(value != null);
assert(value.isNonNegative);
if (_padding == value)
return;
_padding = value;
_markNeedResolution();
}
3 Element
Element是一个Widget的实例,在树中详细的位置。
- Widget描述和配置子树的样子,而Element实际去配置在Element树中特定的位置。
大量的Widget在树结构中存在引用关系,但是Widget会被不断的销毁和重建,那么意味着这棵树非常不稳定; 由Element来维系整个Flutter应用程序的树形结构的稳定
Element什么时候创建?
在每一次创建Widget的时候,会创建一个对应的Element,然后将该元素插入树中。
- Element保存着对Widget的引用;
在SingleChildRenderObjectWidget中,我们可以找到如下代码:
- 在Widget中,Element被创建,并且在创建时,将this(Widget)传入了;
Element就保存了对Widget的引用;
@override
SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
在创建完一个Element之后,Framework会调用mount方法来将Element插入到树中具体的位置
- 在调用mount方法时,会同时使用Widget来创建RenderObject,并且保持对RenderObject的引用