Flutter - CreateState()函数何时执行

3,510 阅读3分钟

首先看一个例子:

class HomePage extends StatefulWidget {
  HomePageState createState() {
    return new HomePageState();
  }
}

class HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        new ChildWidget(),
        RaisedButton(
          child: Text("刷新"),
          onPressed: () => setState(() {}),
        ),
      ],
    );
  }
}

class ChildWidget extends StatefulWidget {
  ChildWidget() {
    developer.log('ChildWidget init', name: "main.dart");
  }

  @override
  State<StatefulWidget> createState() {
    developer.log('ChildWidget createState', name:"main.dart");
    return new ChildWidgetState();
  }
}

class ChildWidgetState extends State<ChildWidget> {
  @override
  Widget build(BuildContext context) {
    developer.log('ChildWidgetState build', name: "main.dart");
    return Text('111');
  }
}

代码很简单,在HomePageState buld函数中,点击刷新按钮,调用setState()方法,build方法再次执行,new ChildWidget()执行,重新new出了一个ChildWidget对象。

Debug程序,点击刷新按钮,看输出日志: 可以看到,ChildWidget createState并没有输出。也就是说,ChildWidgetState对象并没有没有重新创建。这是为什么?

首先了解两个基本概念:
1. Widget实际上就是Element的配置数据,Widget树实际上是一个配置树,而真正的UI渲染树是由Element构成;不过,由于Element是通过Widget生成的,所以它们之间有对应关系,在大多数场景,我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。
2.一个Widget对象(实例)可以对应多个Element对象(实例)。这很好理解,根据同一份配置(Widget),可以创建多个实例(Element)。

就像同一个Widget被插入Widget树的不同位置,就行成了两个Element。

Elment是真正渲染到屏幕上的(通过RenderObject),createState()方法什么时候调用的?来看源码:

class StatefulElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(), //关键代码
        super(widget) {
        //省略...
        }
        //省略...
    }

可以看出只有当StatefulElement构造的时候,createState()方法才会调用! 也就是说,点击刷新按钮的时候,Element对象并没有重新构造。Element需不需要重新构造,是谁决定的?是它的配置Widget决定的,看一下Widget源码中决定Elment是否需要重新构造的函数:

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  const Widget({ this.key });

  @protected
  Element createElement();
  //关键代码
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    bool value = oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
    return value;
  }
}

canUpdate()是一个静态方法,它主要用于在Widget树重新build时复用旧的widget,其实具体来说,应该是:是否用新的Widget对象去更新旧UI树上所对应的Element对象的配置;通过其源码我们可以看到,只要newWidget与oldWidget的runtimeType和key同时相等时就会用newWidget去更新Element对象的配置,否则就会创建新的Element。

Widget中静态函数canUpdate函数决定Element是否需要重新构造,newWidget与oldWidget的runtimeType和key同时相等时就会用newWidget去更新Element对象的配置,否则就会创建新的Element。 所以当点击刷新按钮的时候,new ChildWidget(),newWidget的runtimeType,key和oldWidget的runtimeType,key都是相等的,这意味着不需要重新去创建Element,只要更新Element就可以了。所以createState()函数也就没有执行。

Element不变,State不变!

参考链接: