1.简介
我们知道,Flutter的页面是由很多个Widget组成的,或许你已经知道怎么写页面了,但是你有没有好奇,多个Widget是怎么实现组合成一个页面的呢?
2.StatelessWidget组成的页面
首先我们看看StatelessWidget的源码:
abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key key }) : super(key: key);
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
请先注意createElement
和build
方法。
我们先从一个简单的由StatelessWidget组成的页面开始。
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return First();
}
}
class First extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container();
}
}
在上面的代码中,Flutter通过Widget build(BuildContext context);
方法创建出了一个WidgetTree,内容如下:
在Flutter中,Widget Tree只是界面的蓝图,只是代表了界面上有哪些Widget,并没有包含每个Widget的展示信息。
那么,我们的界面是怎么显示出来的呢?通过Element Tree来决定如何展示界面。在Flutter中,每个Widget都会有一个对应的元素类。Flutter会通过StatelessElement createElement() => StatelessElement(this);
这个方法,创建出Widget的Element元素。当Widget被创建时,会调用该方法创建出元素类,同时挂载到Element Tree上面。我们上面的代码,就会创建出下面的Element Tree。
显示的时候,Flutter会去Element Tree查询到Widget对应的Element然后显示。
image.png
通过Widget Tree和Element Tree,Flutter就能决定界面是如何显示的了。但是,我们是用的StatelessWidget构建的页面是无状态的,界面不会跟着数据的变化而变化。所以此时,我们需要使用StatefulWidget。
3.StatefulWidget组成的页面
先看看StatefulWidget的源码:
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
@protected
@factory
State createState();
}
可以看到createElement
返回了StatefulElement这个元素类。
进入StatefulElement源码中,注意看红框圈起来的内容。
是不是似曾相识?是的,你没有看错,就是StatefulWidget里面的
State createState();
方法。这样,通过StatelessWidget构建Widget Tree和Element Tree的流程,我们就能联系起来了。
- 当创建一个StatefulWidget时,依然会有Widget Tree和Element Tree,只是此时,Element Tree里面是StatefulElement元素类。
- 同时,创建StatefulElement元素类时,StatefulElement会调用StatefulWidget的
createState
获得StatefulWidget的State类,并且保持下来。 - 构建Widget Tree时,会通过调用State的build方法来构建。
-
在State内使用setState方法,能够触发界面的更新。Flutter会根据Element保持的State去刷新Widget。
image.png
这就是StatefulWidget构建页面的过程。我们接下来看看StatefulWidget界面刷新的流程是什么样子的。
4.StatefulWidget界面刷新
先沿用上面的图,当界面构建完成后,如图所示:
image.png
然后,当我们State里面的属性有变化后,State会被Flutter标记。
image.png
此时,我们调用setState方法,触发界面的更新,Flutter会发现State被标记了,那么,他就会创建出一个新的Text。
image.png
然后将原来的Text删除,把新建的Text添加到Widget Tree里面。由于新的Text与老的Text是有相同的类型,所以会使用老的Text的Element来显示自身。
image.png
然后重置State标记,这样,就做到了界面的刷新。
如果此时,MyWidget自身刷新了,情况会如何呢?
和上面一样,Flutter会创建出一个新的MyWidget出来。
image.png
然后用新的替换掉老的,由于新的与老的类型相同,所以依然会使用老的Element对自身进行显示。
image.png
5.总结
WidgetTree:用来描述界面Widget的配置,界面蓝图,我们可以理解为产品经理所做的原型设计。
ElementTree:用来决定Widget如何显示,可以理解为项目经理,协调和调整原型设计与开发。
其实还有一个RenderTree,相当于程序员的角色,对WidgetTree和ElementTree决定的界面进行实现。RenderTree我们平时开发基本接触不到,就不在这里讲了。
网友评论