Flutter-widget生命周期

1,589 阅读5分钟

Flutter-widget生命周期

生命周期

  • 本质: 回调方法(函数)
  • 内容: 让开发者知道当前widget处于什么样的状态
  • 作用
    • 监听widget的事件
    • 初始化数据(创建数据、发送网络请求...)
    • 内存管理(销毁数据、移除监听、销毁timer...)
  • 阶段
    • 初始化(插入渲染树)
    • 状态改变(在渲染树中存在)
    • 销毁(从渲染树种移除)
      图片来自 blog.csdn.net/u011272795/…

Widget生命周期回调函数调用顺序

StatelessWidget

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('life cycle'),
        ),
        body: MyHomePage(title:'hello'),
      ),
    );
  }
}

打印

flutter: +++++++StatelessWidget-构造
flutter: +++++++StatelessWidget-build

PS:也许你在自己测试的时候打印顺序是这样子的,构造方法和build方法被调用了两次,这其实是AS的bug。

flutter: +++++++构造
flutter: +++++++build
flutter: +++++++构造
flutter: +++++++build

你可以使用热重载再运行一次或者终端去运行测试项目,也能得到上面的结果

➜  ~ /Users/ricefun/Desktop/Flutter_case/flutter_app_lifecycle 
➜  flutter_app_lifecycle flutter run
Launching lib/main.dart on iPhone SE (2nd generation) in debug mode...
 
Running Xcode build...                                                  
                                                   
 └─Compiling, linking and signing...                         5.2s
Xcode build done.                                           18.0s
flutter: +++++++构造
flutter: +++++++build                                                   
Syncing files to device iPhone SE (2nd generation)...              128ms

StatefulWidget

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('life cycle'),
        ),
        body: MyHomePage(title:'hello'),
      ),
    );
  }
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState(){
    print('-----------StatefulWidget-State-构造方法');
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    print('-----------StatefulWidget-State-initState()');
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    print('-----------StatefulWidget-State-dispose()');
  }

  @override
  void didUpdateWidget(MyHomePage oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    print('-----------StatefulWidget-State-didUpdateWidget()');
  }

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    print('-----------StatefulWidget-State-didChangeDependencies()');
  }

  //当State对象从渲染树中移出的时候,就会调用!即将销毁!
  @override
  void deactivate() {
    super.deactivate();
    print('-----------StatefulWidget-State-deactivate()');
  }

  @override
  Widget build(BuildContext context) {
    print('-----------StatefulWidget-State-build()');
    return Container();
  }
}

打印

flutter: -----------StatefulWidget_构造方法
flutter: -----------StatefulWidget-State-构造方法
flutter: -----------StatefulWidget-State-initState()
flutter: -----------StatefulWidget-State-didChangeDependencies()
flutter: -----------StatefulWidget-State-build()

didChangeDependencies())(改变依赖关系)

这个方法在上面的打印中显示是在State-initState()之后,貌似没什么用。但是在配合InheritedWidget使用,就会变的有意思。让我们看个列子:

原始:

class _InheritedDemoState extends State<InheritedDemo> {
  int count = 1;//变量
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        //要将count传给Test2
        Test2(count),
        RaisedButton(
          child: Text('我是按钮'),
          //setState!
          onPressed: () => setState(() {
            count++;
          }),
        )
      ],
    );
  }
}

class Test2 extends StatefulWidget {
  //声明变量和构造方法
  final count;
  Test2(this.count);
  @override
  _Test2State createState() => _Test2State();
}

class _Test2State extends State<Test2> {
  @override
  Widget build(BuildContext context) {
    //通过widget 拿到count 数据
    return Text(widget.count.toString());
  }
}

在上面的例子中:在按钮点击后,count数值会变化。在子Widget- Test2 中,声明相同变量final count;通过widget.count去获取数据。当widget树层级非常多的时候,需要每个widget都生命相同变量,那么久会繁琐的多。所以可以使用InheritedWidget

### InheritedWidget:能做到其子Widget能共享InheritedWidget的数据
//数据共享!
class MyData extends InheritedWidget {
  MyData({this.data, Widget child}) : super(child: child);
  final int data; //需要在子Widget中共享的数据!

  //提供一个便捷类方法让子Widget轻松访问的共享数据!
  static MyData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyData>();
  }

  @override
  bool updateShouldNotify(MyData oldWidget) {
    //如果数据发生变化,其依赖InheritedWidget的子Widget就能收到通知,子Widget就会调用build() 
    return oldWidget.data != data;
  }
}


class InheritedDemo extends StatefulWidget {
  @override
  _InheritedDemoState createState() => _InheritedDemoState();
}

class _InheritedDemoState extends State<InheritedDemo> {
  int count = 1;

  @override
  Widget build(BuildContext context) {
    return MyData(//Column被包在了MyData中,MyData其子Widget 都能使用 data字段
      data: count,
      child: Column(
        children: <Widget>[
          Test2(count),
          RaisedButton(
            child: Text('我是按钮'),
            //setState!
            onPressed: () => setState(() {
              count++;
            }),
          )
        ],
      ),
    );
  }
}

class Test2 extends StatefulWidget {
  final count;
  Test2(this.count);
  @override
  _Test2State createState() => _Test2State();
}

class _Test2State extends State<Test2> {
  @override
  Widget build(BuildContext context) {
    print("++++++++++State-build()");
    //通过MyData 提供的方法,可以很方便的拿到count 数据
    return Text(MyData.of(context).data.toString());
  }

  @override
  void didChangeDependencies() {
    print('++++++++++State-didChangeDependencies');
    super.didChangeDependencies();
  }
}

点击打印

flutter: ++++++++++State-didChangeDependencies()
flutter: ++++++++++State-build()
flutter: ++++++++++State-didChangeDependencies
flutter: ++++++++++State-build()
flutter: ++++++++++State-didChangeDependencies
flutter: ++++++++++State-build()
flutter: ++++++++++State-didChangeDependencies
flutter: ++++++++++State-build()

可以看到每次点击,count数值发生变化,都会调用didChangeDependencies()。并且使用InheritedWidget,能都共享数据。 所以得 didChangeDependencies()调用:

  1. initState() 之后调用
  2. 当依赖的InheritedWidget发生变化时,也会调用,并且低调用多次

关于共享数据 InheritedWidget,日常开发中Theme.of(context).textTheme;MediaQuery.of(context).size;都是使用这个技术。pub上有优秀的第三方provider

didUpdateWidget()

文档是上这么说的:Called whenever the widget configuration changes 实际测试中:

  1. 在widget树中,父widget rebuild时,如果当前widget配置有变化,也会调用didUpdateWidget(),并且在调用之后,会调用build()

didUpdateWidget()方法之后会调用build()方法,所以在didUpdateWidget()方法中不要调用setState()

deactivate()

在渲染树中被移除时会先调用deactivate()然后调用dispose()。 eg:组件移除,例如页面销毁的时候会依次执行:deactivate > dispose

dispose()

dispose() 方法在 widget 被废弃时被调用。上面例子没有展示 如果你需要执行一些清理操作(比如:listeners),则重写该方法,并在此之后立即调用 super.dispose()。

总结

StatelessWidget 生命周期函数

基本顺序 函数
1 StatelessWidget-构造函数
2 StatelessWidget-build()

StatefulWidget 生命周期函数

基本顺序 函数 细节 调用次数 是否支持setState
1 StatefulWidget-构造方法 1
2 StatefulWidget-State-构造方法 1
3 StatefulWidget-State-initState() 1
4 StatefulWidget-State-didChangeDependencies() 1.initState() 之后调用会调用一次
2.当依赖的InheritedWidget发生变化时,也会调用,并且低调用多次
>=1
5 StatefulWidget-State-build() >=1
6 StatefulWidget-State-deactivate() >=1
7 StatefulWidget-State-dispose() 先调用deactivate()然后调用dispose() 1
StatefulWidget-State-didUpdateWidget() 在widget树中,父widget rebuild时,如果当前widget配置有变化,也会调用didUpdateWidget(),并且在调用之后,会调用build() >=1

探究

  1. AB个页面是独立的页面,如果AB页面通过bottomNavigationBar放置,它们是生命周期函数调用顺序? 经测试:
flutter: -----------PageA-构造方法
flutter: -----------PageB-构造方法
flutter: -----------PageA-_PageAState-构造方法
flutter: -----------PageA-_PageAState-initState()
flutter: -----------PageA-_PageAState-didChangeDependencies()
flutter: -----------PageA-_PageAState-build()
flutter: -----------PageA-_PageAState-build()
flutter: =========================== A切换到B
flutter: -----------PageA-_PageAState-deactivate()
flutter: -----------PageB-_PageBState-构造方法
flutter: -----------PageB-_PageBState-initState()
flutter: -----------PageB-_PageBState-didChangeDependencies()
flutter: -----------PageB-_PageBState-build()
flutter: -----------PageA-_PageAState-dispose()
  1. BC个页面是独立的页面,重B push到C,又从C pop 到B,它们是生命周期函数调用顺序? 经测试:
flutter: =========================== B push C
flutter: -----------PageC-构造方法
flutter: -----------PageC-_PageCState-构造方法
flutter: -----------PageC-_PageCState-initState()
flutter: -----------PageC-_PageCState-didChangeDependencies()
flutter: -----------PageC-_PageCState-build()
flutter: =========================== C pop B
flutter: -----------PageC-_PageCState-deactivate()
flutter: -----------PageC-_PageCState-dispose()

参考 Flutter widget生命周期详解flutter 生命周期