Element复用:StatefulWidget修改和key

在Flutter中,Widget的功能是“描述一个UI元素的配置数据”,即,Widget其实并不是表示最终绘制在设备屏幕上的显示元素,而只是显示元素的一个配置数据。Flutter中真正代表屏幕上显示元素的类是Element
若类比于编程语言,Widget就像是一个抽象类,而Element才是具体的类实例。
因此,一个Widget对象可能会对应多个Element对象。

渲染流程

  1. 根据用户代码创建Widget树。
  2. 由Widget树创建对应的Element树。
  3. 由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配置:

  1. runtimeType相等
  2. key相等

若两个条件都为true,则表示Element可复用,只使用newWidget来更新Element即可;否则,则表示Element不可复用,需直接创建。
其中:

  1. runtimeType为运行时类型,即该Widget的类。例如,1/2/3的runtimeType为int,“1”/“2”/"3"的runtimeType为String,自定义的Widget其runtimeType为具体的自定义类型。仅仅指类型,不涉及具体的数据。
  2. 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。
进行如下操作:

  1. StatelessW sw = StatelessW(‘text1’),并放在某个容器中。框架的对应逻辑为:

    • 为Widget设置数据text = ‘text1’。
    • 该Widget对应的Element不存在,故会创建新的Element。

    此时,界面上显示文本’text1’。

  2. sw = StatelessW(‘text2’)。框架的对应逻辑为:

    • 为Widget设置数据text = ‘text2’。

    • 该Widget对应的Element存在,于是进入canUpdate()判断:

      • oldWidget.runtimeType为StatelessW,newWidget.runtimeType为StatelessW,相等
      • oldWidget.key为null,newWidget.key为null,相等

      于是,Element判定为可复用,保留。

    • 使用newWidget来更新复用的Element。由于newWidget中text = ‘text2’,故Element要显示的text属性会被更新为’text2’。

    此时,界面上显示文本’text2’。

  3. sw = StatelessW(‘text3’, key: UniqueKey())。框架的对应逻辑为:

    • 为Widget设置数据text = ‘text3’。

    • 该Widget对应的Element存在,于是进入canUpdate()判断:

      • oldWidget.runtimeType为StatelessW,newWidget.runtimeType为StatelessW,相等
      • oldWidget.key为null,newWidget.key为一个随机值,不相等

      于是,Element判定为可不复用,需创建新的Element。

    • 创建新的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。
进行如下操作:

  1. StatefulW sw = StatefulW(‘text1’),并放在某个容器中。框架的对应逻辑为:

    • 为Widget设置数据text = ‘text1’。
    • 该Widget对应的Element不存在,故会创建新的Element。
    • Element创建时,内部的_StatefulWState会一同创建。_StatefulWState内部定义了一个sText,创建时会被赋予初值’text1’。

    显示依赖的是_StatefulWState。此时,界面上显示文本’text1’。

  2. sw = StatefulW(‘text2’)。框架的对应逻辑为:

    • 为Widget设置数据text = ‘text2’。

    • 该Widget对应的Element存在,于是进入canUpdate()判断:

      • oldWidget.runtimeType为StatefulW,newWidget.runtimeType为StatefulW,相等
      • oldWidget.key为null,newWidget.key为null,相等

      于是,Element判定为可复用,保留。其内部的_StatefulWState也随之保留。

    • 使用newWidget来更新复用的Element。由于newWidget中text = ‘text2’,故Element的text属性会被更新为’text2’。

    • 然而,_StatefulWState被保留,故不会再次进入构造函数。于是_StatefulWState中的sText属性依然为’text1’。

    显示依赖的是_StatefulWState。此时,尽管StatefulW的text属性已经更新为’text2’,但_StatefulWState中的sText属性依然为’text1’,故界面上显示文本’text1’。

  3. sw = StatefulW(‘text3’, key: UniqueKey())。框架的对应逻辑为:

    • 为Widget设置数据text = ‘text3’。

    • 该Widget对应的Element存在,于是进入canUpdate()判断:

      • oldWidget.runtimeType为StatefulW,newWidget.runtimeType为StatefulW,相等
      • oldWidget.key为null,newWidget.key为一个随机值,不相等

      于是,Element判定为可不复用,需创建新的Element。

    • 创建新的Element。由于newWidget中text = ‘text3’,故新的Element中text属性是’text3’。

    • Element创建时,内部的_StatefulWState会一同创建。_StatefulWState内部定义了一个sText,创建时会被赋予初值’text3’。

    显示依赖的是_StatefulWState。此时,界面上显示文本’text3’。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章