Flutter 生命周期

Flutter中的生命周期主要包含两部分:

① 组件生命周期

在Flutter开发中,所有的组件和页面都继承自Widget,所以页面的生命周期其实就是Widget的生命周期。

② APP生命周期

主要指应用程序的可见性和响应用户输入的状态。


 一、编写Demo

 截图

 

1.1 main.dart

  • APP入口页面创建MaterialApp
  • 加载PageA页面
import 'package:flutter/material.dart';

import 'package:learn_flutter/page/PageA.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const PageA(title: 'PageA'),
    );
  }
}

1.2 PageA.dart

  • PageA页面是一个有状态的页面
  • 混入WidgetsBindingObserver,监听App生命周期
  • 展示一个按钮,点击跳转PageB页面
import 'package:flutter/material.dart';

import 'package:learn_flutter/page/PageB.dart';

class PageA extends StatefulWidget {
  const PageA({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<PageA> createState() {
    return _PageAState();
  }
}

//混入WidgetsBindingObserver
class _PageAState extends State<PageA> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    //注册观察者
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    super.dispose();
    //移除观察者
    WidgetsBinding.instance.removeObserver(this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            //添加一个按钮
            ElevatedButton(
              onPressed: () {
                //点击按钮跳转PageB页面
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) {
                    return const PageB(title: "PageB");
                  }),
                );
              },
              child: Text("打开PageB"),
            )
          ],
        ),
      ),
    );
  }

  //APP生命周期变化回调
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    print("---log---PageA---${state.name}----");
  }
}

1.3 PageB.dart

  • 重写State生命周期方法
  • const加载TipsView
  • 带参数加载CountView
  • 点击悬浮按钮,会调用setState变化数值
import 'package:flutter/material.dart';

import 'package:learn_flutter/view/CountView.dart';
import 'package:learn_flutter/view/TipsView.dart';

class PageB extends StatefulWidget {
  const PageB({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<PageB> createState() {
    print("---log---PageB---createState----");
    return _PageBState();
  }
}

//重写生命周期方法
class _PageBState extends State<PageB>{
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  void initState() {
    super.initState();
    print("---log---PageB----initState----");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("---log---PageB----didChangeDependencies----");
  }

  //重写setState打印日志
  @override
  void setState(VoidCallback fn) {
    super.setState(fn);
    print("---log---PageB----setState----");
  }

  @override
  Widget build(BuildContext context) {
    print("---log---PageB----build----");
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const TipsView(),//const加载TipsView
            CountView(count: _counter),//带参数加载CountView
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  void didUpdateWidget(covariant PageB oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("---log---PageB----didUpdateWidget----");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("---log---PageB----deactivate----");
  }

  @override
  void dispose() {
    super.dispose();
    print("---log---PageB----dispose----");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("---log---PageB----reassemble----");
  }
}

1.4 CountView.dart

  • 接收参数count,并显示到页面上
  • 重写State生命周期方法
import 'package:flutter/material.dart';

class CountView extends StatefulWidget {
  //接收参数count
  const CountView({Key? key, required this.count}) : super(key: key);

  final int count;

  @override
  State<CountView> createState(){
    print("---log---CountView----createState----");
    return _CountViewState();
  }

}

//重写State生命周期方法
class _CountViewState extends State<CountView> {

  @override
  void initState() {
    //createState执行完后mounted被设置为true
    assert(mounted);

    super.initState();
    print("---log---CountView----initState----");
  }

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

    print("---log---CountView----didChangeDependencies----");
  }

  @override
  Widget build(BuildContext context) {
    print("---log---CountView----build----");
    return Text(
      '${widget.count} ',//使用参数count
      style: Theme.of(context).textTheme.headline4,
    );
  }

  @override
  void didUpdateWidget(covariant CountView oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("---log---CountView----didUpdateWidget----");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("---log---CountView----deactivate----");
  }

  @override
  void dispose() {
    super.dispose();

    //dispose执行完后mounted被设置为false
    assert(!mounted);

    print("---log---CountView----dispose----");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("---log---CountView----reassemble----");
  }

}

1.5 TipsView.dart

  • 重写State生命周期方法
import 'package:flutter/material.dart';

class TipsView extends StatefulWidget {

  const TipsView({Key? key}) : super(key: key);

  @override
  State<TipsView> createState() {
    print("---log---TipsView----createState----");
    return _TipsViewState();
  }
}

class _TipsViewState extends State<TipsView>{

  @override
  void initState() {
    super.initState();
    print("---log---TipsView----initState----");
  }

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

    print("---log---TipsView----didChangeDependencies----");
  }

  @override
  Widget build(BuildContext context) {
    print("---log---TipsView----build----");
    return const Text(
      'You have pushed the button this many times:',
    );
  }

  @override
  void didUpdateWidget(covariant TipsView oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("---log---TipsView----didUpdateWidget----");
  }

  @override
  void deactivate() {
    super.deactivate();

    print("---log---TipsView----deactivate----");
  }

  @override
  void dispose() {
    super.dispose();
    print("---log---TipsView----dispose----");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("---log---TipsView----reassemble----");
  }

}

 下面通过这个简单的Demo,详细介绍Flutter中的生命周期:

二、组件生命周期

在Flutter中一切皆组件,而组件又分为StatefulWidget(有状态)和StatelessWidget(无状态)组件。

2.1 StatelessWidget

无状态组件,没有状态变化,因此只在需要时创建出组件即可,其生命周期很简单,只有构造方法和build两个方法,如下图:

2.2 StatefulWidget

有状态组件,具有动态可交互的内容界面,业务数据或者界面UI状态数据都可能产生变化,所以会根据数据变化进行多次渲染。其生命周期阶段就包含了创建、销毁和更新,这与原生页面的生命周期类似,具体生命周期方法,如下图:

 2.2.1 createState

- StatefulWidget创建时调用

- 执行完毕后,表示当前组件已经在 Widget 树中

- 只调用一次,调用State的构造方法,不可有其他操作

- State类中的mounted属性会被设置为true

2.2.2 initState

- State初始化时调用

- 只会被调用一次,可以做变量初始化操作、接口请求等

 2.2.3 didChangeDependencies

- 组件依赖的 State 发生变化时调用

- didChangeDependencies调用后,会将组件设置为 dirty 状态,将立即调用 build 方法

 2.2.4 build

- 组件创建和绘制阶段调用
- 此方法中应该只包含构建组件的代码,不应该包含其他额外的功能,尤其是耗时任务

2.2.5 didUpdateWidget

- 父组件发生 build 的情况下,子组件该方法才会被调用
- didUpdateWidget调用后,会将组件设置为 dirty 状态,然后调用 build 方法
- 带参数oldWidget,所以常用来将当前组件与前组件进行对比

2.2.6 deactivate

- 组件被移除节点后会被调用

2.2.7 dispose

- 组件被永久移除时调用
- 调用后组件资源被释放,组件生命周期结束
- mounted 属性被设置为 false
- 与initState相对应,只被调用一次

2.2.8 reassemble

- Debug模式下每次热重载时调用
- 仅可以用于调试

2.3 场景

2.3.1  PageA 跳转 PageB (创建)

PageB、TipsView和CountView的生命周期方法执行顺序如下:

I/flutter ( 9382): ---log---PageB---createState----
I/flutter ( 9382): ---log---PageB----initState----
I/flutter ( 9382): ---log---PageB----didChangeDependencies----
I/flutter ( 9382): ---log---PageB----build----
I/flutter ( 9382): ---log---TipsView----createState----
I/flutter ( 9382): ---log---TipsView----initState----
I/flutter ( 9382): ---log---TipsView----didChangeDependencies----
I/flutter ( 9382): ---log---TipsView----build----
I/flutter ( 9382): ---log---CountView----createState----
I/flutter ( 9382): ---log---CountView----initState----
I/flutter ( 9382): ---log---CountView----didChangeDependencies----
I/flutter ( 9382): ---log---CountView----build----

  •  先创建父组件,然后依次创建子组件

2.3.2  PageB 返回 PageA (销毁)

PageB、TipsView和CountView的生命周期方法执行顺序如下:

I/flutter ( 9382): ---log---PageB----deactivate----
I/flutter ( 9382): ---log---TipsView----deactivate----
I/flutter ( 9382): ---log---CountView----deactivate----
I/flutter ( 9382): ---log---TipsView----dispose----
I/flutter ( 9382): ---log---CountView----dispose----
I/flutter ( 9382): ---log---PageB----dispose----

  • 先执行父组件的deactivate方法
  • 再依次执行子组件的deactivate方法
  • 再依次执行子组件的dispose方法
  • 最后执行父组件的dispose方法

2.3.3  PageB调用setState方法(更新)

PageB、TipsView和CountView的生命周期方法执行顺序如下:

I/flutter ( 9868): ---log---PageB----setState----
I/flutter ( 9868): ---log---PageB----build----
I/flutter ( 9868): ---log---CountView----didUpdateWidget----
I/flutter ( 9868): ---log---CountView----build----

  • PageB调用setState后,状态发生变化,所以紧接着会调用build方法
  • CountView的父组件PageB的State发生了变化,所以CountView的didUpdateWidget会被调用
  • CountView的didUpdateWidget将自身标记为dirty状态,所以接着就会执行CountView的build方法
  • 最后因为TipsView被声明为了const,所以PageB的State变化时,TipsView不发生变化,也就不会调用didUpdateWidget方法。

2.4  状态标记

  • State类的mounted属性:用来标识当前组件是否在树中,在 createState 后 initState 前,mounted 会被置为 true,表示当前组件已经在树中。调用 dispose 时,mounted 被置为 false,表示当前组件不在树中。
  • Element类的dirty属性:用来标识当前组件为脏状态,下一帧时将会执行 build 函数。调用 setState 方法或者执行 didUpdateWidget 方法后,组件的状态为 dirty。
  • Element类的clean属性:与 dirty 相对应,clean 表示组件当前的状态为干净状态,clean 状态下组件不会执行 build 函数。

2.5 _StateLifecycle

State的生命周期包含以下四个阶段,创建完成、初始化完成、准备完成、已失效,与生命周期各个方法对应,如下:

enum _StateLifecycle {
  /// 创建完成
  created,

  ///初始化完成
  initialized,

  ///准备完成
  ready,

  /// 已失效
  defunct,
}

三、APP生命周期

3.1 简介

AppLifecycleState 中的状态包括:resumed、inactive、paused、detached,如下:

enum AppLifecycleState {

  /// 该应用程序是可见的,并对用户的输入作出反应,也就是应用程序进入前台。
  resumed,

  /// 应用程序处于非活动状态,没有接收用户的输入。
  /// 在iOS上,这种状态对应的是应用程序或Flutter主机视图在前台非活动状态下运行。
  /// 当处于电话呼叫、响应 TouchID 请求、进入应用切换器或控制中心时,或者当 UIViewController 托管的 Flutter 应用程序正在过渡。
  /// 在Android上,这相当于应用程序或Flutter主机视图在前台非活动状态下运行。
  /// 当另一个活动获取焦点时,如分屏应用、电话呼叫、画中画应用、系统对话框或其他窗口,应用会过渡到这种状态。
  inactive,

  ///该应用程序目前对用户不可见,对用户的输入没有反应,并且在后台运行。
  ///处于此状态时,引擎将不会调用 Window.onBeginFrame 和 Window.onDrawFrame。
  paused,

  /// 应用程序仍然被托管在flutter引擎上,但与任何主机视图分离。
  /// 处于此状态的时机:引擎首次加载到附加到一个平台View的过程中,或者由于执行 Navigator pop view 被销毁。
  detached,

}

3.2 场景

3.2.1 应用从前台进入后台

应用从前台进入后台(例如:点击home键或者电源键),会依次执行以下生命周期方法:

I/flutter ( 9868): ---log---PageA---inactive----
I/flutter ( 9868): ---log---PageA---paused----

3.2.2 应用从后台恢复到前台

应用从后台恢复到前台,会依次执行以下生命周期方法:

I/flutter ( 9868): ---log---PageA---resumed----

3.2.3 返回键退出应用

应用首页点击系统返回键退出应用,会依次执行以下生命周期方法:

I/flutter ( 9868): ---log---PageA---inactive----
I/flutter ( 9868): ---log---PageA---paused----
I/flutter ( 9868): ---log---PageA---detached----

总结

  • 虽然每个有状态的页面都可以监听APP生命周期,但是首页退出应用才能触发到detached方法
  •  后台杀死应用是接收不到生命周期方法回调的
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值