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方法
- 后台杀死应用是接收不到生命周期方法回调的