在StatelessWidget StatefullWidget与Element的关系 博文中简单的说明了二者与Element的逻辑关系。
通过该片博客可以知道Widget提供了一个createElment的抽象方法:
class Widget {
Element createElement();
}
到此,本篇博客的设计的前提知识已经具备,下面具体分析下Widget的关系。
下面就Widget与Element的关系在做进一步的分析和巩固。
在Widget中有如下几种常见的Widget,其继承关系如图所示:
那么再来看看Element的图示:
乍一看上下两幅图是一样的,其实因为Widget提供了createElement这个抽象方法,本身就说明每个不同的Widget都有对应的Element对象。
在这里着重说一下RenderObjectWidget,看下表:
Widget | 说明 | 举例 |
---|---|---|
MultiChildRenderObjectWidget | 该类型的Widget可以添加多个widget | Row,Column,Stack |
SingleChildRenderObjectWIdget | 该类型的widget只能添加一个widget | Center,Padding,Container |
LeafRenderObjectWidget | 该类型是树的叶子节点,故不能添加widget | Text,Image,Semantics |
注意其实Text的父类是StatelessWidget,但是其build方法返回的Widget是LeadfRenderObjectWidget的子类,所以在这里为了方便说明,也将Text归于LeadRenderObjectWidget了。了解了上面的基本信息后,看下面的代码进行分析:
Widget _createWidget(){
return Center(
child: Container(
child: Row(
children: [
Text("A"),
Text("B")
],
),
),
);
}
上面createWidget方法组成的Widget树状图以及对应的Element的树状图如下所示:
从上面的图中我们可以清楚的看到Widget和Element的对应关系。那么Widget的createElement是神马时候初始化的呢?根据Flutter官方文档对Element的注释说明,我们需要查看Element的mount方法。经过博主研究建议读者阅读MultiChildRenderObjectWidget对象对应的MultiChildRenderObjectElement,直接查看MultiChildRenderObjectElement的mout方法即可:
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_children = List(widget.children.length);
Element previousChild;
for (int i = 0; i < _children.length; i += 1) {
final Element newChild = inflateWidget(widget.children[i], previousChild);
_children[i] = newChild;
previousChild = newChild;
}
}
在mount方法中可以看出循环遍历了MultiChildRenderObjectWidget的children集合,为集合中的每一个child widget调用inflateWidget方法,该方法返回了child widget对应的Element对象。注意inflateWidget方法由两个参数,一个是children集合中的某个Widget,一个是Element对象。
所以接下来看看inflateWidget方法都做了神马:
Element inflateWidget(Widget newWidget, dynamic newSlot) {
//省略部分更新Widget的逻辑。
//创建childWidget的具体Element对象
final Element newChild = newWidget.createElement();
//继续调用childElement的mount方法。
newChild.mount(this, newSlot);
return newChild;
}
排除了与本文无关的相关代码之后,inflateWidget代码逻辑很简单:
1、调用widget的createElement方法来创建Widget对应的Element对象(终于知道Widget的createElement方法什么时候调用的了吧)
2、调用步骤1创建的Element的mount方法,如果新创建的Elment是MultiChildRenderObjectElement的话,则会继续调用该MultiChildRenderObjectElement的mount,并循环其children循环执行inflateWidget方法,也就是重复步骤1.
下面在用一个例子代码来讲解其具体过程,该过程可以回答在StatelessWidget StatefullWidget与Element的关系 这篇博客遗留的问题:
Row(
children: [
Text("A"),
Text("B")
],
)
比如上面的这段代码,因为Row这个组件父类是MultiChildRenderObjectWidget,其对应的Elmenent是MultiChildRenderObjectElement.其中Row包含了两个child Widget 即两个Text。那么根据上面的mount分析就需要循环遍历两个Text,先调用Text的createElement方法创建其Element对象。因为Text extends StatelessWidget.所以Text.createElement返回的就是StatelessElement.
此处借用Flutter之StatelessWidget StatefulWidget和Element关系浅析
这篇博客的一幅图来说明其流程:
而Text的build方法返回的就是一个LeafRenderObjectWidget,整体流程如下:
到此为止本篇博文就结束了,如有不当之处欢迎批评指正,共同学习