这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战android
前言:生命周期是一个组件加载到卸载的整个周期,熟悉生命周期可让咱们在合适的时机作该作的事情, flutter中的State生命周期和android以及React Native的生命周期相似。markdown
先看一张生命周期的流程图:app
大体能够分为3个阶段:框架
State初始化时会依次执行 : 构造函数 > initState > didChangeDependencies > Widget build , 此时页面加载完成。async
而后咱们看一下每一个函数的意义:ide
调用次数:1次函数
这个函数严格意义上来说不属于生命周期的一部分,由于这个时候State的widget属性为空,没法在构造函数中访问widget的属性 。可是构造函数必然是要第一个调用的。能够在这一部分接收前一个页面传递过来的数据。布局
Called when this object is inserted into the tree.post
调用次数:1次ui
当插入渲染树的时候调用,这个函数在生命周期中只调用一次。这里能够作一些初始化工做,好比初始化State的变量。
Called when a dependency of this [State] object changes.
这个函数会紧跟在initState以后调用,而且能够调用BuildContext.inheritFromWidgetOfExactType,那么BuildContext.inheritFromWidgetOfExactType的使用场景是什么呢?最经典的应用场景是
new DefaultTabController(length: 3, child: new TabBar(
tabs: [ "主页","订单","个人" ]
.map( (data)=>new Text(data) ).toList(),
复制代码
TabBar原本须要定义一个TabController,可是在外面套一层DefaultTabController就不须要定义TabContrller了,看下源码:
@override
void didChangeDependencies() {
super.didChangeDependencies();
_updateTabController();
_initIndicatorPainter();
}
void _updateTabController() {
final TabController newController = widget.controller ?? DefaultTabController.of(context);
...
}
复制代码
注意到这里DefaultTabController.of(context)
static TabController of(BuildContext context) {
final _TabControllerScope scope = context.inheritFromWidgetOfExactType(_TabControllerScope);
return scope?.controller;
}
复制代码
实际上就是调用BuildContext.inheritFromWidgetOfExactType,也就说在didChangeDependencies中,能够跨组件拿到数据。
#运行时
调用次数:屡次
初始化以后开始绘制界面,当setState触发的时候会再次被调用
Called whenever the widget configuration changes.
祖先节点rebuild widget时调用 .当组件的状态改变的时候就会调用didUpdateWidget.
理论上setState的时候会调用,但我实际操做的时候发现只是作setState的操做的时候没有调用这个方法。而在我改变代码hot reload时候会调用 didUpdateWidget 并执行 build...
实际上这里flutter框架会建立一个新的Widget,绑定本State,并在这个函数中传递老的Widget。 这个函数通常用于比较新、老Widget,看看哪些属性改变了,并对State作一些调整。
须要注意的是,涉及到controller的变动,须要在这个函数中移除老的controller的监听,并建立新controller的监听。
组件移除,例如页面销毁的时候会依次执行:deactivate > dispose
Called when this object is removed from the tree.
在dispose以前,会调用这个函数。当组件卸载时会先一步dispose调用。
Called when this object is removed from the tree permanently.
调用次数:1次
一旦到这个阶段,组件就要被销毁了,这个函数通常会移除监听,清理环境。
##reassemble hot reload调用
名称 | 状态 |
---|---|
initState | 插入渲染树时调用,只调用一次 |
didChangeDependencies | state依赖的对象发生变化时调用 |
didUpdateWidget | 组件状态改变时候调用,可能会调用屡次 |
build | 构建Widget时调用 |
deactivate | 当移除渲染树的时候调用 |
dispose | 组件即将销毁时调用 |
假设咱们从A页面跳转到B页面, 那么A,B页面的生命周期会是怎样的呢?
B页面进入初始化状态,依次执行4个函数:构造函数 > initState > didChangeDependencies > Widget build
, 此时页面加载完成,进入运行态。 此时A页面依次执行deactivate > build
函数。注意 此时A页面并未卸载。
而后咱们假设B页面只有一个按钮,点击B页面中的按钮,改变按钮的文字,会执行widget的build
方法 ,(理论上也应该执行didUpdateWidget
,但我这里没有)。
这时,咱们点击返回键从B页面返回到A页面。 A页面从新显示,B页面开始卸载。 那么A先执行deactivate > build
, 而后B页面依次执行:deactivate > dispose
。 此时A页面进入运行态,B页面移除。
本次示例B页面代码:
/*
* Created by 李卓原 on 2018/9/13.
* email: zhuoyuan93@gmail.com
*
*/
import 'package:flutter/material.dart';
class NewsDetailPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => NewsDetailState();
}
class NewsDetailState extends State<NewsDetailPage> {
int text = 1;
NewsDetailState() {
print('构造函数');
}
@override
void initState() {
print('init state');
super.initState();
}
@override
void didChangeDependencies() {
print('didChangeDependencies');
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
print('widget build');
return Scaffold(
body: Center(
child: _loading(),
),
appBar: AppBar(
title: Text('咨询详情'),
),
);
}
@override
void didUpdateWidget(NewsDetailPage oldWidget) {
print('组件状态改变:didUpdateWidget');
super.didUpdateWidget(oldWidget);
}
@override
void deactivate() {
print('移除时:deactivate');
super.deactivate();
}
@override
void dispose() {
print('移除时:dispose');
super.dispose();
}
//预加载布局
Widget _loading() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
strokeWidth: 1.0,
),
Container(
child: Text("正在加载"),
margin: EdgeInsets.only(top: 10.0),
)
],
);
}
}
复制代码
下面内容来自咸鱼技术团队.
当ListView中的item滚动出可显示区域的时候,item会被从树中remove掉,此item子树中全部的state都会被dispose,state记录的数据都会销毁,item滚动回可显示区域时,会从新建立全新的state、element、renderobject
使用hot reload功能时,要特别注意state实例是没有从新建立的,若是该state中存在一下复杂的资源更新须要从新加载才能生效,那么须要在reassemble()添加处理,否则当你使用hot reload时候可能会出现一些意想不到的结果,例如,要将显示本地文件的内容到屏幕上,当你开发过程当中,替换了文件中的内容,可是hot reload没有触发从新读取文件内容,页面显示仍是原来的旧内容.
didChangeDependencies有两种状况会被调用。
建立时候在initState 以后被调用
在依赖的InheritedWidget发生变化的时候会被调用
正常的退出流程中会执行deactivate而后执行dispose。可是也会出现deactivate之后不执行dispose,直接加入树中的另外一个节点的状况。
这里的状态改变包括两种可能:1.经过setState内容改变 2.父节点的state状态改变,致使孩子节点的同步变化。
须要指出的是若是想要知道App的生命周期,那么须要经过WidgetsBindingObserver的didChangeAppLifecycleState 来获取。经过该接口能够获取是生命周期在AppLifecycleState类中。经常使用状态包含以下几个:
名称 | 状态 |
---|---|
resumed | 可见并能响应用户的输入 |
inactive | 处在并不活动状态,没法处理用户响应 |
paused | 不可见并不能响应用户的输入,可是在后台继续活动中 |
一个实际场景中的例子:
在不考虑suspending的状况下:从后台切入前台生命周期变化以下: AppLifecycleState.inactive->AppLifecycleState.resumed;
从前台压后台生命周期变化以下: AppLifecycleState.inactive->AppLifecycleState.paused;
class _MyWalletScreenState extends State<MyWalletScreen>
with WidgetsBindingObserver {
@override void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
void didChangeAppLifecycleState(AppLifecycleState state) async {
if (state == AppLifecycleState.resumed) {
getData();
}
}
}
复制代码
记得注册和移除监听。