Flutter视图Widget生命周期

4,190 阅读4分钟

Flutter视图Widget生命周期

作为一个Android开发者,一定会对Activity的生命周期有这很深刻的印象,而当你在使用Flutter时,其中Widget就是View,其生命周期就是从View创建到销毁的过程。 Widget分为StatelessWidgetStatefulWidget 两种,这两种Widget的生命周期分别如下。

StatelessWidget的生命周期

无状态Widget的生命周期很简单,它只有一个生命周期:build

build

build函数用来构建视图,每次页面刷新是被调用,典型的用法如下:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          child: new Text('Hello World'),
        ),
      ),
    );
  }
}

StatefulWidget的生命周期

StatefulWidget生命周期整理如下图:

大致可分为三个阶段:

  • 初始化:插入渲染树
  • 运行中:在渲染树中存在
  • 销毁:从渲染树中移除
初始化阶段
createState

createState必须且仅执行一次,它用来创建state,当创建StatefulWidget时,该放方法被执行

initState

在创建StatefulWidget后,initState是第一个被调用的方法,同createState一样只被调用一次,此时widget的被添加至渲染树,mount的值会变为true,但并没有渲染。可以在该方法内做一些初始化操作。在在override时要低啊用super.initState()

@override
void initState() {
  super.initState();
  ...
}
didChangeDependencies

当widget第一次被创建时,didChangeDependencies紧跟着initState函数之后调用,在widget刷新时,该方法不会被调用。它会在“依赖”发生变化时被Flutter Framework调用,这个依赖是指widget是否使用父widget中InheritedWidget的数据。也即是只有在widget依赖的InheritedWidget发生变化之后,didChangeDependencies才会调用。 这种机制可以使子组件在所依赖的InheritedWidget变化时来更新自身!比如当主题、locale(语言)等发生变化时,依赖其的子widget的didChangeDependencies方法将会被调用。

//通过继承InheritedWidget,将当前计数器点击次数保存在ShareDataWidget的data属性中:
class ShareDataWidget extends InheritedWidget {
  ShareDataWidget({
    @required this.data,
    Widget child
  }) :super(child: child);

  final int data; //需要在子树中共享的数据,保存点击次数

  //定义一个便捷方法,方便子树中的widget获取共享数据  
  static ShareDataWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }

  //该回调决定当data发生变化时,是否通知子树中依赖data的Widget  
  @override
  bool updateShouldNotify(ShareDataWidget old) {
    //如果返回true,则子树中依赖(build函数中有调用)本widget
    //的子widget的`state.didChangeDependencies`会被调用
    return old.data != data;
  }
}

class _TestWidget extends StatefulWidget {
  @override
  __TestWidgetState createState() => new __TestWidgetState();
}
//然后我们实现一个子组件_TestWidget,在其build方法中引用ShareDataWidget中的数据。
class __TestWidgetState extends State<_TestWidget> {
  @override
  Widget build(BuildContext context) {
    //使用InheritedWidget中的共享数据
    return Text(ShareDataWidget
        .of(context)
        .data
        .toString());
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
    //如果build中没有依赖InheritedWidget,则此回调不会被调用。
    print("Dependencies change");
  }
}

注意:如果_TestWidget的build方法中没有使用ShareDataWidget的数据,那么它的didChangeDependencies()将不会被调用,因为它并没有依赖ShareDataWidget。在依赖改变之后build方法也会被调用,所以在大多数场景下都无需使用didChangeDependencies。然而如果你需要在依赖改变后执行一些昂贵的操作,比如网络请求,这时最好的方式就是在此方法中执行,这样可以避免每次build()都执行这些昂贵操作。

build

build函数会在widget第一次创建时紧跟着didChangeDependencies方法之后和UI重新渲染是时调用。build只做widget的创建操作,如果在build里做其他操作,会影响UI的渲染效果

运行中
didUpdateWidget

当组件的状态改变的时候就会调用didUpdateWidget,比如调用了setState.

销毁
deactivate

当要将State对象从渲染树中移除的时候,就会调用 deactivate 生命周期,这标志着 StatefulWidget将要销毁。页面切换时,也会调用它,因为此时State在视图树中的位置发生了变化但是State不会被销毁,而是重新插入到渲染树中。 重写的时候必须要调用 super.deactivate()

dispose

从渲染树中移除view的时候调用,State会永久的从渲染树中移除,和initState正好相反mount值变味false。这时候就可以在dispose里做一些取消监听操作。

总结:
函数 调用次数 调用时间
createState 1 第一次创建
initState 1 第一次创建
didChangeDependencies n 第一次创建和依赖变化时
build n 第一次创建和UI重新渲染时
didUpdateWidget n 第一次创建和UI重新渲染时
deactivate n state对象将要移除时
dispose 1 state对象移除