Flutter 生命周期包括了组件的生命周期以及App的生命周期。
一、组件生命周期
一个flutter组件主要分为无状态组件(StatelessWidget)和有状态组件(StatefulWidget),无状态组件只在创建的时候渲染一次,创建完成后不能重新渲染,而有状态组件可以根据交互或数据变化等,进行多次渲染,本文组件生命周期专指有状态组件的生命周期。
组件生命周期主要是:createState、initState、didChangeDependencies、reassemble、didUpdateWidget、build、deactivate、dispose这几个函数,从函数名我们可以初步感知其作用,它们大致的流转关系及作用如图:
下面以一个简单的Demo验证这个流程
1.构建一个简单的MyApp:
import 'package:flutter/material.dart';
import 'package:flutterlifecycledemo/ui/home_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
/// 初始化state,只会在第一次构建组件时调用一次
///
/// 在此期间可以执行State各变量的初始赋值
/// 同时可以在此处调用网络请求,服务器返回数据后通过setState刷新页面,类似于iOS中的ViewDidLoad方法
@override
void initState() {
super.initState();
print('initState_MyApp');
}
/// 该组件所依赖的State发生变化时触发,或者在initState(组件首次创建)后触发
///
/// 该组件所依赖的State指全局的State,例如语言和主题等
/// 此方法会触发build
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies_MyApp');
}
/// 在debug模式下,每次热重载都会触发该函数
///
/// 主要是在开发阶段在此增加一些debug代码,检查代码问题
@override
void reassemble() {
super.reassemble();
print('reassemble_MyApp');
}
/// 组件重新构建(如热重载),父组件发生build的情况下触发
///
/// 注意:
/// 父组件发生build,本组件(子组件)该方法才会被调用,并且该方法调用后一定会再调用本组件中的build方法
/// 并且父组件首次创建不会触发
@override
void didUpdateWidget(MyApp oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget_HomePage');
}
/// 组件在被移除节点后被调用
///
/// 如果该组件被移除节点,然后未被插入到其他节点时,则会继续调用dispose永久移除
@override
void deactivate() {
super.deactivate();
print('deactivate_MyApp');
}
/// 永久移除组件,释放组件资源
@override
void dispose() {
super.dispose();
print('dispose_MyApp');
}
/// 构建需要渲染的widget,会被多次调用(会被setState/didChangeDependencies/didUpdateWidget触发)
///
/// 只做返回Widget的相关逻辑
@override
Widget build(BuildContext context) {
print('build_MyApp');
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Color(0xff409eff),
brightness: Brightness.light, // 默认状态栏颜色
),
home: HomePage(),
);
}
}
2.HomPage实现:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
HomePage();
/// 创建state
///
/// 当StatefulWidget被调用时会立即执行
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State {
bool showProgressHud = true;
double cw = 60;
/// 模拟加载数据
void loadData(){
// 5秒后调用setState
Future.delayed(Duration(seconds: 5), (){
setState(() {
showProgressHud = false;
});
});
}
/// 初始化state,只会在第一次构建组件时调用一次
///
/// 在此期间可以执行State各变量的初始赋值
/// 同时可以在此处调用网络请求,服务器返回数据后通过setState刷新页面,类似于iOS中的ViewDidLoad方法
@override
void initState() {
super.initState();
print('initState_HomePage');
loadData();
}
/// 该组件所依赖的State发生变化时触发,或者在initState(组件首次创建)后触发
///
/// 该组件所依赖的State指全局的State,例如语言和主题等
/// 此方法会触发build
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies_HomePage');
}
/// 在debug模式下,每次热重载都会触发该函数
///
/// 主要是在开发阶段在此增加一些debug代码,检查代码问题
@override
void reassemble() {
super.reassemble();
print('reassemble_HomePage');
}
/// 组件重新构建(如热重载),父组件发生build的情况下触发
///
/// 注意:
/// 父组件发生build,本组件(子组件)该方法才会被调用,并且该方法调用后一定会再调用本组件中的build方法
/// 并且父组件首次创建不会触发
@override
void didUpdateWidget(HomePage oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget_HomePage');
}
/// 组件在被移除节点后被调用
///
/// 如果该组件被移除节点,然后未被插入到其他节点时,则会继续调用dispose永久移除
@override
void deactivate() {
super.deactivate();
print('deactivate_HomePage');
}
/// 永久移除组件,释放组件资源
@override
void dispose() {
super.dispose();
print('dispose_HomePage');
}
/// 构建需要渲染的widget,会被多次调用(会被setState/didChangeDependencies/didUpdateWidget触发)
///
/// 只做返回Widget的相关逻辑
@override
Widget build(BuildContext context) {
print('build_HomePage');
return Scaffold(
appBar: AppBar(
title: Text('Hello Flutter',style: TextStyle(color: Color(0xffffffff),fontSize: 16, fontWeight: FontWeight.bold),),
centerTitle: true,
actions: [
FlatButton( // 触发setState,showProgressHud=true,当前HomeConent组件会被移除
child: Text('loadData',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
showProgressHud = true;
loadData();
});
},
),
FlatButton( // 增加cw值,并只触发setState,showProgressHud不变,当前SubHomePage组件不会被移除
child: Text('click',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
cw += 10;
});
},
),
],
),
body: Container(
decoration: BoxDecoration(
color: Color(0xffffffff),
),
child: Center(
child: showProgressHud ?
CircularProgressIndicator() :
Column(
mainAxisAlignment: MainAxisAlignment.center ,
children: [
Container(width: cw, height: 44,color: Color(0xff889eff),),
SubHomePage()
],
)
,
),
),
);
}
}
3.SubHomePage实现:
class SubHomePage extends StatefulWidget {
@override
_SubHomePageState createState() => _SubHomePageState();
}
class _SubHomePageState extends State {
/// 初始化state,只会在第一次构建组件时调用一次
///
/// 在此期间可以执行State各变量的初始赋值
/// 同时可以在此处调用网络请求,服务器返回数据后通过setState刷新页面,类似于iOS中的ViewDidLoad方法
@override
void initState() {
super.initState();
print('initState_SubHomePage');
}
/// 该组件所依赖的State发生变化时触发,或者在initState(组件首次创建)后触发
///
/// 该组件所依赖的State指全局的State,例如语言和主题等
/// 子类很少重写这个方法,非必要情况不用重写此方法
/// 此方法会触发build
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies_SubHomePage');
}
/// 在debug模式下,每次热重载都会触发该函数
///
/// 主要是在开发阶段在此增加一些debug代码,检查代码问题
@override
void reassemble() {
super.reassemble();
print('reassemble_SubHomePage');
}
/// 组件重新构建(如热重载),父组件发生build的情况下触发
///
/// 注意:
/// 父组件发生build,本组件(子组件)该方法才会被调用,并且该方法调用后一定会再调用本组件中的build方法
/// 并且父组件首次创建不会触发
@override
void didUpdateWidget(SubHomePage oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget_SubHomePage');
}
/// 组件在被移除节点后被调用
///
/// 如果该组件被移除节点,然后未被插入到其他节点时,则会继续调用dispose永久移除
@override
void deactivate() {
super.deactivate();
print('deactivate_SubHomePage');
}
/// 永久移除组件,释放组件资源
@override
void dispose() {
super.dispose();
print('dispose_SubHomePage');
}
/// 构建需要渲染的widget,会被多次调用(会被setState/didChangeDependencies/didUpdateWidget触发)
///
/// 只做返回Widget的相关逻辑
@override
Widget build(BuildContext context) {
print('build_SubHomePage');
return Column(
children: [
Container(height: 100,),
Text('Welcome to flutter', style: TextStyle(color: Color(0xff333333), fontSize: 16),),
Container(
height: 10,
),
FlatButton(
onPressed: increaseAction,
child: Text(
'Increase',
style: TextStyle(color: Color(0xffffffff), fontSize: 14),
),
color: Color(0xff409eff),
)
],
);
}
void increaseAction(){
setState(() {
});
}
}
页面效果:
首次加载控制台输出:
正好验证了首次加载有状态组件的生命周期,其中build_HomePage打印了两次,是因为HomePage在initState的时候调用了模拟网络请求方法loadData,在请求完毕后调用了setState,从而触发了HomePage的build。
接着点击导航栏上的loadData(属于HomePage的组件)按钮,触发:
FlatButton( // 触发setState,showProgressHud=true,当前SubHomePage组件会被移除
child: Text('loadData',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
showProgressHud = true;
loadData();
});
},
),
此时控制台输出:
我们发现首次生成的SubHomePage被移除了,并且loadData完成后生成新的SubHomePage,新的SubHomePage会接着走新的首次加载流程
child: Center(
child: showProgressHud ?
CircularProgressIndicator() :
Column(
mainAxisAlignment: MainAxisAlignment.center ,
children: [
Container(width: cw, height: 44,color: Color(0xff889eff),),
SubHomePage()
],
)
,
),
此时我们点HomePage导航栏上的click按钮 ,增加矩形的宽度:
FlatButton( // 增加cw值,并只触发setState,showProgressHud不变,当前SubHomePage组件不会被移除
child: Text('click',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
cw += 10;
});
},
),
控制台输出:
此时我们可以看到HomePage的build触发了其子组件SubHomePage的didUpdateWidget和build方法,但是SubHomePage仍然是原来的SubHomePage,并未重新释放(如果释放后重构会调用dispose和initState),只是重新build了一次。
点击SubHomePage上的increase,控制台输出:
cmd+\ 热重载,控制台输出:
二、App生命周期
app生命周期比较简单,具体可以参考官方文档