咱们在学习 Flutter 的时候,可能常常看到三个名词: Widget、RenderObject 和 Element ,弄懂这几个概念可能也是入门 Flutter 框架原理的第一步。编程
在 Flutter 中,万物皆是 Widget,不管是可见的仍是功能型的,那么 Widget 到底是什么呢?bash
按照惯例,先看官方文档。微信
接下来看一下 Widget 的定义。框架
abstract class Widget {
const Widget({ this.key });
final Key key;
@protected
Element createElement();
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
复制代码
经过上面 Widget 的定义,能够看到有两个重要的方法,一个是经过 createElement 来建立 Element 对象的,一个是根据 key 来决定更新行为的 canUpdate 方法。ide
以 Opacity 为例,Opacity 作为一个 Widget ,只保存另外一个配置信息:opacity,这个属性决定了透明度,范围在 0 到 1 之间。函数
Opacity 既然作为一个 Widget,确定是 Widget 的子类,其继承关系以下:布局
Opacity → SingleChildRenderObjectWidget → RenderObjectWidget → Widget
复制代码
Opacity 的定义以下:学习
class Opacity extends SingleChildRenderObjectWidget {
const Opacity({
Key key,
@required this.opacity,
this.alwaysIncludeSemantics = false,
Widget child,
}) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
assert(alwaysIncludeSemantics != null),
super(key: key, child: child);
final double opacity;
@override
RenderOpacity createRenderObject(BuildContext context) {
return RenderOpacity(
opacity: opacity,
alwaysIncludeSemantics: alwaysIncludeSemantics,
);
}
@override
void updateRenderObject(BuildContext context, RenderOpacity renderObject) {
renderObject
..opacity = opacity
..alwaysIncludeSemantics = alwaysIncludeSemantics;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('opacity', opacity));
properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
}
}
复制代码
经过上面的代码,能够看到 opacity 是 final 类型的属性,只能作为构造函数参数传递进去,不可改变,所以若是要更新这个属性,必须新建一个 Opcity 对象,这也是为何咱们代码里的 Widget build(BuildContext context) 方法里面每次 build 都会建立新的对象实例的缘由。ui
RenderObject 的定义以下this
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
ParentData parentData;
Constraints _constraints;
void layout(Constraints constraints, { bool parentUsesSize = false }) {
}
void paint(PaintingContext context, Offset offset) { }
void performLayout();
void markNeedsPaint() {
}
}
复制代码
经过以上定义,能够看出,RenderObject 的主要做用就是绘制和布局的。
那么这个 RenderObject 是哪里来的呢?是在 Widget 里面建立的。如上面的 Opacity 中重写了 createRenderObject 方法来建立 RenderOpacity 对象。
@override
RenderOpacity createRenderObject(BuildContext context) {
return RenderOpacity(
opacity: opacity,
alwaysIncludeSemantics: alwaysIncludeSemantics,
);
}
复制代码
在 RenderOpacity 内部实现了布局、点击检测和大小计算等功能。
Element 的 生命周期:
Flutter framework 经过 Widget.createElement 来建立一个 element 。
每当 Widget 建立并插入到 Widget 树中时,framework 就会经过 mount 方法来把这个 widget 建立并关联的 element 插入到 element 树中(其父 element 会给出一个位置)。
经过 attachRenderObject 方法来将 render objects 来关联到 render 树上,这时能够认为这个 widget 已经显示在屏幕上了。
每当执行了 rebuid 方法,widget 表明的配置信息改变时(建立了一个新的 widget),framewrok 就会调用这个新的 widget 的 update 方法(新的 widget 的 和老的 widget 有相同的 runtimeType 和 key,若是不一样,就要先 unmounting 而后从新装载 widget)。
当 element 的祖先想要移除一个子 element 时,能够经过 deactivateChild 方法,先把这个 element 从 树中移除,而后将这个 element 加入到一个“不活跃元素列表”中,接着 framework 就会将这个 element 从屏幕移除(当下一个渲染帧到来这个 element 依然不活跃)。
因为 是在 Widget 中建立了Element,相似于 Widget 的继承关系,Element 的继承关系以下:
SingleChildRenderObjectElement → RenderObjectElement → Element
复制代码
接着看一下 Opacity 中,如何建立一个 Element 。 Opacity 继承自 SingleChildRenderObjectElement,在 SingleChildRenderObject 中建立了 Element
@override
SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this);
复制代码
在 RenderObjectElement 中提供了 mount 方法
abstract class RenderObjectElement extends Element {
RenderObjectElement(RenderObjectWidget widget) : super(widget);
RenderObject _renderObject;
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
}
复制代码
经过上面的代码,咱们可以发现,Element 中经过 widget.createRenderObject 方法也拿到了 RenderObject 对象,所以 Element 实际上是同时包含 RenderObject 和 Widget 。
mount 方法会将 element 插入到 element 树中,mount 中还会调用 attachRenderObject 方法。
abstract class RenderObjectElement extends Element {
@override
void attachRenderObject(dynamic newSlot) {
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
}
复制代码
在这个方法里,经过 _findAncestorRenderObjectElement 方法, 找到了Element树上的祖先Element,若是祖先不为空,就调用insertChildRenderObject方法,这个方法的意思就是把renderObject的child替换成newSlot,而后经过 _updateParentData 用于更新布局数据的一些信息。
上面只是简单介绍了一下 Flutter 中的 Widget 、RenderObject 和 Element 中的概念,而 Widget,Element和RenderObject体系是Flutter框架的核心 至于内部原理以及若是工做的,须要结合 Flutter 框架结构运行原理来看,这样才能更好的理解这些概念。
参考:
Flutter, what are Widgets, RenderObjects and Elements