在Flutter中,Widget的功能是“描述一个UI元素的配置数据”,即,Widget其实并不是表示最终绘制在设备屏幕上的显示元素,而只是显示元素的一个配置数据。Flutter中真正代表屏幕上显示元素的类是Element。
若类比于编程语言,Widget就像是一个抽象类,而Element才是具体的类实例。
因此,一个Widget对象可能会对应多个Element对象。
渲染流程
- 根据用户代码创建Widget树。
- 由Widget树创建对应的Element树。
- 由Element树生成Render树。
最后,Render树经过布局、绘制等,在界面上显示出来。
由于具体的显示都是Element控制,因此可以认为Element是显示在界面上的元素。
Element的复用
出于效率原因,Element是可复用的。
对于新的一帧,Widget会判断每个Element是否可复用。若是,则保留Element,并用新的Widget配置来更新;否则,则创建新的Element。
也就是说,对于一帧新画面上的Element,要么更新,要么重建。
Widget负责判断Element是否可复用。Widget的判断源码为:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
判断条件很简单,根据前后帧的两个Widget配置:
- runtimeType相等
- key相等
若两个条件都为true,则表示Element可复用,只使用newWidget来更新Element即可;否则,则表示Element不可复用,需直接创建。
其中:
- runtimeType为运行时类型,即该Widget的类。例如,1/2/3的runtimeType为int,“1”/“2”/"3"的runtimeType为String,自定义的Widget其runtimeType为具体的自定义类型。仅仅指类型,不涉及具体的数据。
- key即用户传入的key。若用户没有传入,则为null。(null == null)返回true。
实例: StatelessWidget
定义一个StatelessWidget:
class StatelessW extends StatelessWidget {
String text = '';
StatelessW(this.text, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(text);
}
}
如上,该StatelessW接收1个父组件传入的String。
进行如下操作:
-
StatelessW sw = StatelessW(‘text1’),并放在某个容器中。框架的对应逻辑为:
- 为Widget设置数据text = ‘text1’。
- 该Widget对应的Element不存在,故会创建新的Element。
此时,界面上显示文本’text1’。
-
sw = StatelessW(‘text2’)。框架的对应逻辑为:
-
为Widget设置数据text = ‘text2’。
-
该Widget对应的Element存在,于是进入
canUpdate()判断:- oldWidget.runtimeType为
StatelessW,newWidget.runtimeType为StatelessW,相等 - oldWidget.key为null,newWidget.key为null,相等
于是,Element判定为可复用,保留。
- oldWidget.runtimeType为
-
使用newWidget来更新复用的Element。由于newWidget中text = ‘text2’,故Element要显示的text属性会被更新为’text2’。
此时,界面上显示文本’text2’。
-
-
sw = StatelessW(‘text3’, key: UniqueKey())。框架的对应逻辑为:
-
为Widget设置数据text = ‘text3’。
-
该Widget对应的Element存在,于是进入
canUpdate()判断:- oldWidget.runtimeType为
StatelessW,newWidget.runtimeType为StatelessW,相等 - oldWidget.key为null,newWidget.key为一个随机值,不相等
于是,Element判定为可不复用,需创建新的Element。
- oldWidget.runtimeType为
-
创建新的Element。由于newWidget中text = ‘text3’,故新的Element中要显示的text属性是’text3’。
此时,界面上显示文本’text3’。
-
实例: StatefulWidget
定义一个StatefulWidget:
class StatefulW extends StatefulWidget {
String text = '';
StatefulW(this.text, {Key key}) : super(key: key);
@override
_StatefulWState createState() => _StatefulWState(text);
}
class _StatefulWState extends State<StatefulW> {
String sText = '';
_StatefulWState(this.text) {
this.sText = widget.text;
}
@override
Widget build(BuildContext context) {
return Text(sText);
}
}
如上,该StatefulW接收1个父组件传入的String。
进行如下操作:
-
StatefulW sw = StatefulW(‘text1’),并放在某个容器中。框架的对应逻辑为:
- 为Widget设置数据text = ‘text1’。
- 该Widget对应的Element不存在,故会创建新的Element。
- Element创建时,内部的_StatefulWState会一同创建。_StatefulWState内部定义了一个sText,创建时会被赋予初值’text1’。
显示依赖的是_StatefulWState。此时,界面上显示文本’text1’。
-
sw = StatefulW(‘text2’)。框架的对应逻辑为:
-
为Widget设置数据text = ‘text2’。
-
该Widget对应的Element存在,于是进入
canUpdate()判断:- oldWidget.runtimeType为
StatefulW,newWidget.runtimeType为StatefulW,相等 - oldWidget.key为null,newWidget.key为null,相等
于是,Element判定为可复用,保留。其内部的_StatefulWState也随之保留。
- oldWidget.runtimeType为
-
使用newWidget来更新复用的Element。由于newWidget中text = ‘text2’,故Element的text属性会被更新为’text2’。
-
然而,_StatefulWState被保留,故不会再次进入构造函数。于是_StatefulWState中的sText属性依然为’text1’。
显示依赖的是_StatefulWState。此时,尽管StatefulW的text属性已经更新为’text2’,但_StatefulWState中的sText属性依然为’text1’,故界面上显示文本’text1’。
-
-
sw = StatefulW(‘text3’, key: UniqueKey())。框架的对应逻辑为:
-
为Widget设置数据text = ‘text3’。
-
该Widget对应的Element存在,于是进入
canUpdate()判断:- oldWidget.runtimeType为
StatefulW,newWidget.runtimeType为StatefulW,相等 - oldWidget.key为null,newWidget.key为一个随机值,不相等
于是,Element判定为可不复用,需创建新的Element。
- oldWidget.runtimeType为
-
创建新的Element。由于newWidget中text = ‘text3’,故新的Element中text属性是’text3’。
-
Element创建时,内部的_StatefulWState会一同创建。_StatefulWState内部定义了一个sText,创建时会被赋予初值’text3’。
显示依赖的是_StatefulWState。此时,界面上显示文本’text3’。
-

本文聚焦于Flutter开发,介绍了Widget和Element的关系,Widget是显示元素的配置数据,Element才是真正显示在界面上的元素。阐述了渲染流程,包括创建Widget树、Element树、Render树并最终显示。还说明了Element的复用规则,通过runtimeType和key判断,最后给出StatelessWidget和StatefulWidget的实例。
666

被折叠的 条评论
为什么被折叠?



