【老孟Flutter】Stateful 组件的生命周期
老孟导读:关于生命周期的文章共有2篇,第一篇是介绍 Flutter 中Stateful 组件的生命周期。
博客地址:http://laomengit.com/blog/20201227/Stateful%E7%BB%84%E4%BB%B6%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html第二篇是 Flutter 中与平台相关的生命周期,
博客中还有更多精彩文章,也欢迎加入 Flutter 交流群。
此篇文章介绍 StatefulWidget 组件的生命周期, StatefulWidget 组件的生命周期时非常重要的知识点,就像 Android 中 Activity 的生命周期一样,不仅在以后的工作中经常用到,面试也会经常被问到。
在 Flutter 中一切皆 组件,而组件又分为 StatefulWidget(有状态) 和 StatelessWidget(无状态)组件 ,他们之间的区别是 StatelessWidget 组件发生变化时必须重新创建新的实例,而 StatefulWidget 组件则可以直接改变当前组件的状态而无需重新创建新的实例。
注意:使用的 Flutter 版本 和 Dart 版本如下:
Flutter 1.22.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 1aafb3a8b9 (6 weeks ago) • 2020-11-13 09:59:28 -0800
Engine • revision 2c956a31c0
Tools • Dart 2.10.4不同的版本 StatefulWidget 组件的生命周期会有差异。
下面的 StatefulWidget 和 State 结构图是StatefulWidget 组件生命周期的概览,不同版本的差异也可以对比此结构图。
生命周期流程图:
下面详细介绍 StatefulWidget 组件的生命周期。
生命周期一:createState
下面是一个非常简单的 StatefulWidget 组件:
class StatefulWidgetDemo extends StatefulWidget {
@override
_StatefulWidgetDemoState createState() => _StatefulWidgetDemoState();
}
class _StatefulWidgetDemoState extends State<StatefulWidgetDemo> {
@override
Widget build(BuildContext context) {
return Container();
}
}
当我们构建一个 StatefulWidget 组件时,首先执行其构造函数(上面的代码没有显示的构造函数,但有默认的无参构造函数),然后执行 createState 函数。但构造函数并不是生命周期的一部分。
当 StatefulWidget 组件插入到组件树中时 createState 函数由 Framework 调用,此函数在树中给定的位置为此组件创建 State,如果在组件树的不同位置都插入了此组件,即创建了多个此组件,如下:
Row(children: [
MyStatefulWidget(),
MyStatefulWidget(),
MyStatefulWidget(),
],)
那么系统会为每一个组件创建一个单独的 State,当组件从组件树中移除,然后重新插入到组件树中时, createState 函数将会被调用创建一个新的 State。
createState 函数执行完毕后表示当前组件已经在组件树中,此时有一个非常重要的属性 mounted 被 Framework 设置为 true。
生命周期二:initState
initState 函数在组件被插入树中时被 Framework 调用(在 createState 之后),此函数只会被调用一次,子类通常会重写此方法,在其中进行初始化操作,比如加载网络数据,重写此方法时一定要调用 super.initState(),如下:
@override
void initState() {
super.initState();
//初始化...
}
如果此组件需要订阅通知,比如 ChangeNotifier 或者 Stream,则需要在不同的生命周期内正确处理订阅和取消订阅通知。
- 在 initState 中订阅通知。
- 在 didUpdateWidget 中,如果需要替换旧组件,则在旧对象中取消订阅,并在新对象中订阅通知。
- 并在 dispose 中取消订阅。
另外在此函数中不能调用 BuildContext.dependOnInheritedWidgetOfExactType,典型的错误写法如下:
@override
void initState() {
super.initState();
IconTheme iconTheme = context.dependOnInheritedWidgetOfExactType<IconTheme>();
}
异常信息如下:
解决方案:
@override
void didChangeDependencies() {
super.didChangeDependencies();
context.dependOnInheritedWidgetOfExactType<IconTheme>();
}
上面的用法作为初学者使用的比较少,但下面的错误代码大部分应该都写过:
@override
void initState() {
super.initState();
showDialog(context: context,builder: (context){
return AlertDialog();
});
}
异常信息如下:
解决方案:
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showDialog(context: context,builder: (context){
return AlertDialog(title: Text('AlertDialog'),);
});
});
}
注意:弹出 AlertDialog 在 didChangeDependencies 中调用也会出现异常,但和上面的异常不是同一个。
生命周期三:didChangeDependencies
didChangeDependencies 方法在 initState 之后由 Framework 立即调用。另外,当此 State 对象的依赖项更改时被调用,比如其所依赖的 InheritedWidget 发生变化时, Framework 会调用此方法通知组件发生变化。
此方法是生命周期中第一个可以使用 BuildContext.dependOnInheritedWidgetOfExactType 的方法,此方法很少会被重写,因为 Framework 会在依赖发生变化时调用 build,需要重写此方法的场景是:依赖发生变化时需要做一些耗时任务,比如网络请求数据。
didChangeDependencies 方法调用后,组件的状态变为 dirty,立即调用 build 方法。
生命周期四:build
此方法是我们最熟悉的,在方法中创建各种组件,绘制到屏幕上。 Framework会在多种情况下调用此方法:
- 调用 initState 方法后。
- 调用 didUpdateWidget 方法后。
- 收到对 setState 的调用后。
- 此 State 对象的依存关系发生更改后(例如,依赖的 InheritedWidget 发生了更改)。
- 调用 deactivate 之后,然后将 State 对象重新插入树的另一个位置。
此方法可以在每一帧中调用,此方法中应该只包含构建组件的代码,不应该包含其他额外的功能,尤其是耗时任务。
生命周期五:didUpdateWidget
当组件的 configuration 发生变化时调用此函数,当父组件使用相同的 runtimeType 和 Widget.key 重新构建一个新的组件时,Framework 将更新此 State 对象的组件属性以引用新的组件,然后使用先前的组件作为参数调用此方法。
@override
void didUpdateWidget(covariant StatefulLifecycle oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget');
}
此方法中通常会用当前组件与前组件进行对比。Framework 调用完此方法后,会将组件设置为 dirty 状态,然后调用 build 方法,因此无需在此方法中调用 setState 方法。
生命周期六:deactivate
当框架从树中移除此 State 对象时将会调用此方法,在某些情况下,框架将重新插入 State 对象到树的其他位置(例如,如果包含该树的子树 State 对象从树中的一个位置移植到另一位置),框架将会调用 build 方法来提供 State 对象适应其在树中的新位置。
生命周期七:dispose
当框架从树中永久移除此 State 对象时将会调用此方法,与 deactivate 的区别是,deactivate 还可以重新插入到树中,而 dispose 表示此 State 对象永远不会在 build。调用完 dispose后,mounted 属性被设置为 false,也代表组件生命周期的结束,此时再调用 setState 方法将会抛出异常。
子类重写此方法,释放相关资源,比如动画等。
非常重要的几个概念
下面介绍几个非常重要的概念和方法,这些并不是生命周期的一部分,但是生命周期过程中的产物,与生命周期关系非常紧密。
mounted
mounted 是 State 对象中的一个属性,此属性表示当前组件是否在树中,在创建 State 之后,调用 initState 之前,Framework 会将 State 和 BuildContext 进行关联,当 Framework 调用 dispose 时,mounted 被设置为 false,表示当前组件已经不在树中。
createState 函数执行完毕后表示当前组件已经在组件树中,属性 mounted 被 Framework 设置为 true,平时写代码时或者看其他开源代码时经常看到如下代码:
if(mounted){
setState(() {
...
});
}
强烈建议:在调用 setState 时加上 mounted 判断。
为什么要加上如此判断?因为如果当前组件未插入到树中或者已经从树中移除时,调用 setState 会抛出异常,加上 mounted 判断,则表示当前组件在树中。
dirty 和 clean
dirty 表示组件当前的状态为 脏状态,下一帧时将会执行 build 函数,调用 setState 方法或者 执行 didUpdateWidget 方法后,组件的状态为 dirty。
clean 与 dirty 相对应,clean 表示组件当前的状态为 干净状态,clean 状态下组件不会执行 build 函数。
setState
setState 方法是开发者经常调用的方法,此方法调用后,组件的状态变为 dirty,当有数据要更新时,调用此方法。
reassemble
reassemble 用于开发,比如 hot reload ,在 release 版本中不会回调此方法。
交流
老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |
【老孟Flutter】Stateful 组件的生命周期的更多相关文章
- 【老孟Flutter】Flutter 中与平台相关的生命周期
老孟导读:关于生命周期的文章共有2篇,一篇(此篇)是介绍 Flutter 中Stateful 组件的生命周期. 第二篇是 Flutter 中与平台相关的生命周期, 博客地址:http://laomen ...
- 【老孟Flutter】为什么 build 方法放在 State 中而不是在 StatefulWidget 中
老孟导读:此篇文章是生命周期相关文章的番外篇,在查看源码的过程中发现了这一有趣的问题,欢迎大家一起探讨. Flutter 中Stateful 组件的生命周期:http://laomengit.com/ ...
- 【老孟Flutter】源码分析系列之InheritedWidget
老孟导读:这是2021年源码系列的第一篇文章,其实源码系列的文章不是特别受欢迎,一个原因是原理性的知识非常枯燥,我自己看源码的时候特别有感触,二是想把源码分析讲的通俗易懂非常困难,自己明白 和 让别人 ...
- 【老孟Flutter】41个酷炫的 Loading 组件库
老孟导读:目前 loading 库中包含41个动画组件,还会继续添加,同时也欢迎大家提交自己的 loading 动画组件或者直接微信发给我也可以. Github 地址:https://github.c ...
- 【老孟Flutter】自定义文本步进组件
交流 老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com 欢迎加入Flutter交流群(微信:laomengit).关注公众号[老孟Flutter] ...
- react.js 从零开始(二)组件的生命周期
什么是生命周期? 组件本质上是一个状态机,输入确定,输出一定确定. 当状态改变的时候 会触发不同的钩子函数,可以让开发者做出响应.. 一个组件的生命周期可以概括为 初始化:状态下 可以自定义的函数 g ...
- React Native组件、生命周期及属性传值props详解
创建组件的三种方式 第一种:通过ES6的方式创建 /** * 方式一 :ES6 */ export default class HelloComponent extends Component { r ...
- 【老孟Flutter】2020年总结
2020年是我经历的最不平凡的一年,这一年有遗憾.有收获,有感概,也有庆幸,庆幸自己还活着. 用一句话总结自己的2020,忙并收获着,累并快乐着. <Flutter 实战入门> <F ...
- 【老孟Flutter】2021 年 Flutter 官方路线图
老孟导读:这是官方公布的2021年路线图,向我们展示了2021年 Flutter 的主要工作及计划. 原文地址:https://github.com/flutter/flutter/wiki/Road ...
随机推荐
- 「刷题笔记」DP优化-状压-EX
棋盘 需要注意的几点: 题面编号都是从0开始的,所以第1行实际指的是中间那行 对\(2^{32}\)取模,其实就是\(unsigned\ int\),直接自然溢出啥事没有 棋子攻击范围不会旋转 首先, ...
- Alpha冲刺-第六次冲刺笔记
Alpha冲刺-冲刺笔记 这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE2 这个作业要求在哪里 https://edu.cnblogs. ...
- 从七牛云迁移图片到github
迁移理由 问题是网站的大部分图床都是用的七牛云,官网有改动,所以原测试域名都失效,所以决定进行迁移,将七牛云中的图片迁移到github仓库中. 迁移步骤 Step1:从废弃测试域名空间至可用测试域名空 ...
- springboot多模块项目搭建遇到的问题记录
废话不多说,直接上问题报错与解决方法. 问题报错一:(报错信息看下方代码) 问题原因:'com.company.logistics.service.company.CompanyService' 未找 ...
- Python中函数是否能使用全局变量?
Python函数中的变量,既可以使用局部变量(本地名字空间的变量),也可以使用全局变量(全局名字空间的变量),函数在执行查找变量只读时,先在局部变量中查找,找不到再查到全局变量中查找.因此当局部变量和 ...
- OLLVM简单入门
目前市面上的许多安全公司都会在保护IOS应用程序或安卓APP时都会用到OLLVM技术.譬如说顶象IOS加固.网易IOS加固等等.故而我们今天研究下OLLVM是个什么.将从(1)OLLVM是什么?OLL ...
- 安恒2018年三月月赛MISC蜘蛛侠呀
到处都是知识盲区hhh 下载了out.pcap之后,里面有很多ICMP包 看到ttl之后联想到西湖论剑里面的一道杂项题,无果 看WP知道可以使用wireshark的tshark命令提取流量包里面的文件 ...
- Python3安装且环境配置(三)
1.在Window 平台安装 Python3 以下为在 Window 平台上安装 Python3 的简单步骤: 打开WEB浏览器访问http://www.python.org/download/ 在下 ...
- LeetCode初级算法之数组:66 加一
加一 题目地址:https://leetcode-cn.com/problems/plus-one/ 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一.最高位数字存放在数组的首位, 数 ...
- js监测页面是否切换到后台
最近做个弹幕,用的是第三方的插件,在浏览器页面切换到后台,返回后发现数据有堆叠卡死的情况,如何解决这个问题?网上参考了些demo,大致可以实现 1.document.hidden( Boolean值, ...