在前面的章节中我们从Flutter的基础组件到高阶组件由浅入深的进行了系统的巩固学习,其中还包括Flutter中的数据存储,网络请求,利用路由进行页面切换传值,经过了这一系列的知识体系学习巩固,我们基本把Flutter入门所需要储备的所有知识学习完毕,今天我们在之前课程的基础上做一期巩固提高的分享,来一起探索下Flutter中关于组件的生命周期。
熟悉移动端开发的朋友对生命周期这个概念都不会陌生,如在Android、iOS、React、Vue中都会有生命周期这一概念,它定义了组件从初始化到被加载再到中间过渡态的状态改变直至被销毁整个一系列的存在过程。
结合上述流程图中对Flutter中定义的各个生命周期段大致可以简短概括为三大阶段:
createState 是 StatefulWidget 里创建 State 的方法,当要创建新的 StatefulWidget 的时候,会立即执行 createState,而且只执行一次,createState 必须要实现:
class LifeCycleDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() => _LifeCycleState();
}
前面的 createState 是在创建 StatefulWidget 的时候会调用,initState 是 StatefulWidget 创建完后调用的第一个方法,而且只执行一次,类似于 Android 的 onCreate、IOS 的 viewDidLoad(),所以在这里 View 并没有渲染,但是这时 StatefulWidget 已经被加载到渲染树里了,这时 StatefulWidget 的 mount 的值会变为 true,直到 dispose 调用的时候才会变为 false。可以在 initState 里做一些初始化的操作。
当 StatefulWidget 第一次创建的时候,didChangeDependencies 方法会在 initState 方法之后立即调用,之后当 StatefulWidget 刷新的时候,就不会调用了,除非你的 StatefulWidget 依赖的 InheritedWidget 发生变化之后,didChangeDependencies 才会调用,所以 didChangeDependencies 有可能会被调用多次。
在 StatefulWidget 第一次创建的时候,build 方法会在 didChangeDependencies 方法之后立即调用,另外一种会调用 build 方法的场景是,每当 UI 需要重新渲染的时候,build 都会被调用,所以 build 会被多次调用,然后 返回要渲染的 Widget。千万不要在 build 里做除了创建 Widget 之外的操作,因为这个会影响 UI 的渲染效率。
didUpdateWidget 这个生命周期我们一般不会用到,只有在使用 key 对 Widget 进行复用的时候才会调用。
当要将 State 对象从渲染树中移除的时候,就会调用 deactivate 生命周期,这标志着 StatefulWidget 将要销毁,但是有时候 State 不会被销毁,而是重新插入到渲染树种。
当 View 不需要再显示,从渲染树中移除的时候,State 就会永久的从渲染树中移除,就会调用 dispose 生命周期,这时候就可以在 dispose 里做一些取消监听、动画的操作,和 initState 是相反的。
下面我们来通过一个简单的小例子通过打印log的方式来记录一下在Flutter中组件从被加载
到状态改变
,再到被移除
的整个生命周期变化过程。
import 'package:flutter/material.dart';
class LifeCycleDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() => _LifeCycleState();
}
class _LifeCycleState extends State<LifeCycleDemo> {
var _count = 0;
@override
void initState() {
super.initState();
print('----------initState------------');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('----------didChangeDependencies------------');
}
@override
Widget build(BuildContext context) {
print('----------build------------');
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
child: Text('关闭页面'),
onPressed: () => {
Navigator.of(context).pop()},
),
Text('当前计数器数量 : '+_count.toString()),
RaisedButton(
child: Text('刷新界面'),
onPressed: () => {
this.setState(() {
_count++;
print('----------刷新界面------------');
})
},
)
],
),
),
);
}
@override
void deactivate() {
super.deactivate();
print('----------deactivate------------');
}
@override
void dispose() {
super.dispose();
print('----------dispose------------');
}
@override
void didUpdateWidget(LifeCycleDemo oldWidget) {
super.didUpdateWidget(oldWidget);
print('----------didUpdateWidget------------');
}
}
通过上述代码,我们模拟来一个简单的组件被加载到页面上时,从初始化到整个节目完成渲染后,组件经历了从initState–>didChangeDependencies–>build的过程后,如果此时组件没有更新state到操作,界面将会一直保持在build渲染完成之后的状态,对应各个生命周期回调函数打印log如下所示:
我们知道在Flutter中StatefulWidget
通过State
来管理整个页面的状态变化,当state
发生变化时,及时通知到相应的组件发生改变,当页面状态发生更新时生命周期中的build回调函数会被重新唤起然后重新渲染界面,对组件状态做出的变化及时更新渲染到UI上。
如上图,当我点击两次刷新界面按钮,通过setState
来改变_count
的值,然后组件感知到state发生变化,重新调用build函数,刷新界面直至新的状态渲染在UI上。
当页面被销毁时,组件的的生命周期状态从deactivate–>dipose变化,此过程多为用户点击返回或者调用Navigator.of(context).pop()
路由推出当前页面后发生回调。此时用户可以根据具体的业务需求做一些取消监听、动画的操作。
通过开篇对各个生命周期函数的存在意义的讲解读者可利用上述生命周期测试的示例代码自行仿照示例代码重新写一遍,感知一下组件生命周期存在的意义以及不同生命周期回调函数回调的时机,以加深理解。