Flutter —— 生命周期

179 阅读5分钟

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

1. 生命周期

一个对象从创建到销毁所经历的过程就是一个生命周期,说白了就是回调方法。在Flutter中已经封装好了,知道widget处于什么样的状态了,然后给你一个对应状态的回调,所以说生命周期其实是一系列的回调方法。

那么生命周期有什么作用呢?

  • 初始化数据
    • 创建变量,常量
    • 发送网络请求
  • 监听小部件的事件
  • 管理内存
    • 销毁数据,销毁监听者
    • 销毁timer等等

2. Widget的生命周期

Widget主要分两种,StatelessWidget 和 StatefulWidget。这里先分析StatelessWidget的生命周期

2.1 StatelessWidget常见的生命周期

新建一个项目,然后在MyApp里面将home里面的widget改成Scaffold。

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,
      ),
      home: Scaffold(
        appBar: AppBar(),
        body: MyHomePage(title: 'Flutter Demo Page 2',),
      ),
    );
  }
}

然后将MyHomePage改成StatelessWidget,这里title是可选参数且允许为空,那么在使用的时候可以使用强制解包,或者使用??来让title为空的时候使用右边的值,那么这里不传title的话那么Text里面就会显示的是123。

class MyHomePage extends StatelessWidget {

  final String? title;

  MyHomePage({this.title});


  @override
  Widget build(BuildContext context) {
    return Center(child: Text(title ?? "123"),);
  }
}

然后在构造函数以及build函数里面添加打印,然后根据打印来看调用的顺序和时机。

class MyHomePage extends StatelessWidget {

  final String? title;

  MyHomePage({this.title}){
    print('构造函数被调用了');
  }


  @override
  Widget build(BuildContext context) {
    print('build函数被调用了');
    return Center(child: Text(title ?? "123"),);
  }
}

这里android studio 运行的话会有小小的问题就是打印两次,用xcode运行就不会有这个问题了。这里可以看到构造函数是在build函数之前被调用的。

g.csdnimg.cn/e53b1460fd834e2a8adb7d1b78655932.png)

2.1 StatefulWidget常见的生命周期

将MyHomePage改成StatefulWidget,然后在Widget构造函数,createState函数,State构造函数,initState函数,dispose函数以及build函数里面分别添加打印方法。

class MyHomePage extends StatefulWidget {

  final String? title;
  MyHomePage({this.title}){
    print('Widget构造函数被调用了');
  }

  @override
  State<MyHomePage> createState() {
    print('createState函数被调用了');
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {

  _MyHomePageState() {
    print('State构造函数被调用了');
  }
  @override
  void initState() {
    // TODO: implement initState
    print('initState函数被调用了');
    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    print('dispose函数被调用了');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print('build函数被调用了');
    return Scaffold(
      appBar: AppBar(
      ),
      body: Center(child: Text(widget.title ?? '123'),)
    );
  }
}

运行后观察打印顺序,发现是Widget构造函数 —— createState函数 —— State构造函数 —— initState函数 —— build函数 —— dispose函数,这里没有调用dispose函数是因为没有被销毁。

在这里插入图片描述

那么在setState的时候调用了哪些方法呢?修改一下_MyHomePageState。

class _MyHomePageState extends State<MyHomePage> {
 int _count = 0;
  _MyHomePageState() {
    print('State构造函数被调用了');
  }
  @override
  void initState() {
    // TODO: implement initState
    print('initState函数被调用了');
    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    print('dispose函数被调用了');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print('build函数被调用了');
    return Column(
      children: [
        ElevatedButton(onPressed: (){
         setState(() {
           _count ++;
         });
        }, child: const Icon(Icons.add)),
        Text('$_count'),
      ],
    );
  }
}

热重载后点击按钮,发现热重载调用了Widget构造函数和build函数。setState调用了build函数。

在这里插入图片描述

点进去setState的源码里面看到,setState其实只做了一件事就是_element!.markNeedsBuild()。

在这里插入图片描述

这里的_element是StatefulElement类型,并且在上面可以看到context就是_element。

在这里插入图片描述

那么是否可以通过调用markNeedsBuild来代替setState呢?实验一下

 @override
  Widget build(BuildContext context) {
    print('build函数被调用了');
    return Column(
      children: [
        ElevatedButton(onPressed: (){
           _count ++;

         (context as StatefulElement).markNeedsBuild();
        }, child: const Icon(Icons.add)),
        Text('$_count'),
      ],
    );
  }

运行后发现是可以的,并且屏幕上的text也有改变。当然这里还是推荐使用setState,因为系统在setState里面做了很多判断。

在这里插入图片描述

statefulWidget里面还有一个didChangeDependencies的回调方法。

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
  }

会在initState函数和build函数之间被调用。didChangeDependencies是改变依赖关系的回调。

在这里插入图片描述

创建一个InheritedDemo部件来试验这个didChangeDependencies,这里设置多个层级test1,test2,test3。

import 'package:flutter/material.dart';

class InheritedDemo extends StatefulWidget {
  const InheritedDemo({Key? key}) : super(key: key);

  @override
  _InheritedDemoState createState() => _InheritedDemoState();
}

class _InheritedDemoState extends State<InheritedDemo> {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Test1(_count),
        ElevatedButton(onPressed: (){
          setState(() {
            _count++;
          });
        }, child: Text('我是按钮'))
      ],
    );
  }
}

class Test1 extends StatelessWidget {
  final int count;
  const Test1(this.count);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Test2(count);
  }
}

class Test2 extends StatelessWidget  {
  final int count;
  const Test2(this.count);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Test3(count);
  }
}
class Test3 extends StatefulWidget {
  final int count;
  const Test3(this.count);

  @override
  _Test3State createState() => _Test3State();
}

class _Test3State extends State<Test3> {
  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    print('didChangeDependencies来了');
    super.didChangeDependencies();
  }
  @override
  Widget build(BuildContext context) {
      print('build来了');
    return Text(widget.count.toString());
  }
}

到MyApp里面使用 InheritedDemo

 home: Scaffold(
        appBar: AppBar(),
        body: InheritedDemo(
        ),
      ),

这个时候就可以共享count,但是这样方式太繁琐了,那么就创建一个MyData类。

class MyData  extends InheritedWidget {
  final int data;//需要在子组件中共享的数据(保存点击次数)
  // 构造方法
  const MyData({required this.data,required Widget child}):super(child: child);

  // 定义一个便捷方法,方便子组件中的Widget 去获取共享的数据
  static MyData? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyData>();
  }

  // 该回调决定当前data发生变化的时候是否通知子组件依赖data的Widget
  @override
  bool updateShouldNotify(covariant MyData oldWidget) {
      // 如果返回true,子部件中依赖数据的Widget(build函数中有使用数据)的didChangeDependencies会被调用
      return oldWidget.data != data;
  }
}

在_InheritedDemoState中使用MyData。

class _InheritedDemoState extends State<InheritedDemo> {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    return MyData(data: _count, child:  Column(
      children: [
        Test1(_count),
        ElevatedButton(onPressed: (){
          setState(() {
            _count++;
          });
        }, child: Text('我是按钮'))
      ],
    ));
  }
}

那么这个时候,点击按钮的时候,就会调用didChangeDependencies函数 ,并且在build函数之前。如果updateShouldNotify返回false那么就不会调用didChangeDependencies函数。didChangeDependencies函数 主要是用在如果有很多逻辑依赖这个数据的情况下,那么就可以在didChangeDependencies保存这个数据。

在这里插入图片描述

在MyData里面的子部件才能共享数据,如果希望整个项目共享那么就在home里面就包 一个MyData。

在这里插入图片描述

3. 总结

  • 生命周期的基本概念
    • 什么是生命周期
      • 说白了就是回调方法(函数)
      • 让你知道我封装好的这个Widget它处于什么样的状态了!
    • 有什么作用
      • 监听Widget的事件
      • 初始化数据
        • 创建数据
        • 发送网络请求
      • 内存管理
        • 销毁数据、销毁监听者
        • 销毁Timer等等
  • Widget的生命周期
    • Stateless
      • 1、构造方法
      • 2、build方法
    • Stateful(包含两个对象Widget、State)
      • Widget构造方法
      • Widget的CreateState
      • State的构造方法
      • State的initState方法
      • didChangeDependencies方法(改变依赖关系)
        • 依赖的InheritedWidget发生变化之后,方法也会调用!
      • State的build
        • 当调用setState方法。会重新调用build进行渲染!
      • 当Widget销毁的时候,调用State的dispose