深入理解Flutter生命周期函数之StatefulWidget(一)
目录
前言
1.为什么需要生命周期函数
2.开发过程中常用的生命周期函数
1.initState()
2.didChangeDependencies()
3.build()
4.didUpdateWidget()
5.setState()
6.deactivate()
7.dispose()
3.Flutter生命周期总结
1.调用顺序
2.函数调用时机以及主要作用
4.生命周期函数的验证
1.创建和销毁时期函数的验证
2.didUpdateWidget函数验证
3.didChangeDependencies函数验证
前言
在Flutter中,生命周期函数是管理StatefulWidget状态的关键机制。通过生命周期函数,我们可以控制Widget的初始化、更新和销毁过程,使得应用的状态管理和资源控制更加灵活。本文将详细介绍Flutter中的生命周期函数,帮助你更好地掌握Flutter应用的生命周期。
1.为什么需要生命周期函数
生命周期函数允许我们在Widget的创建、更新和销毁过程中执行特定操作,比如数据的初始化、网络请求、资源的释放等。尤其是在StatefulWidget中,这些函数确保了应用在不同状态下的正确行为。
2.开发过程中常用的生命周期函数
1.initState()
这个方法在State对象被插入到树中时调用,仅调用一次。
适合做初始化操作,例如初始化变量、加载数据或创建动画控制器。
@override
void initState() {super.initState();debugPrint("initState method is called!");
}
2.didChangeDependencies()
这个方法在initState()调用之后,或者当依赖的InheritedWidget发生变化时调用。
这个方法适用于需要访问依赖于上下文的情况,比如当Widget依赖于某个InheritedWidget的变化。
下面的代用于打印"didChangeDependencies method is called!"。
@override
void didChangeDependencies() {super.didChangeDependencies();debugPrint("didChangeDependencies method is called!");
}
3.build()
build函数是构建UI的核心函数。
build()在每次Widget需要重建时都会调用,包括在初次加载时、调用setState()之后。
build方法用于描述Widget在屏幕上的展示方式。这个函数会频繁调用,因此确保代码尽量简洁高效。
在这段代码中,build()函数返回一个带有计数器的页面,并包含一个按钮用于增加计数器。
@override
Widget build(BuildContext context) {debugPrint("build method is called!");return Scaffold(appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary,title: Text(widget.title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:',),Text('$_counter',style: Theme.of(context).textTheme.headlineMedium,),ElevatedButton(onPressed: (){// 跳转逻辑}, child: const Text('跳转下一个页面')),],),),floatingActionButton: FloatingActionButton(onPressed: _incrementCounter,tooltip: 'Increment',child: const Icon(Icons.add),),);
}
4.didUpdateWidget()
调用时机:当父Widget重新构建,并将新的Widget传递给子Widget时调用。
作用:适合在父Widget属性变化时执行相应操作,比如更新状态或重新初始化数据。
在下面代码中,它打印"didUpdateWidget method is called!",用于展示父级属性更新时的情况。
@override
void didUpdateWidget(covariant MyHomePage oldWidget) {super.didUpdateWidget(oldWidget);debugPrint("didUpdateWidget method is called!");
}
5.setState()
调用时机:在状态变化时,通过手动调用setState()来触发。
作用:通知Flutter框架状态已改变,触发build()方法重新构建Widget。注意避免频繁调用setState()以减少性能开销。
@override
void didUpdateWidget(covariant MyHomePage oldWidget) {super.didUpdateWidget(oldWidget);debugPrint("didUpdateWidget method is called!");
}
6.deactivate()
调用时机:当State对象被临时从树中移除时调用。
作用:可以在这里执行一些临时清理工作。通常不需要在这里执行大量操作,使用场景不多。
在这段代码中,deactivate()打印了"deactivate method is called!"。
@override
void deactivate() {super.deactivate();debugPrint("deactivate method is called!");
}
7.dispose()
调用时机:当State对象永久性地从树中移除时调用。
作用:适合释放资源,比如取消订阅、关闭控制器等。
在代码中,dispose()打印了"dispose method is called!",用来展示页面被销毁时的情况。
@override
void dispose() {super.dispose();debugPrint("dispose method is called!");
}
3.Flutter生命周期总结
1.调用顺序
通过上面的介绍,可以看到StatefulWidget的生命周期有以下顺序:
- 创建阶段:initState() → didChangeDependencies()
- 更新阶段:build() → didUpdateWidget() → setState()
- 销毁阶段:deactivate() → dispose()
每个函数在Widget生命周期中扮演着不同的角色:
2.函数调用时机以及主要作用
函数 | 调用时机 | 主要作用 |
initState() | Widget被插入树中时调用一次 | 初始化操作,仅调用一次 |
didChangeDependencies() | initState()后及依赖变化时调用 | 处理依赖项 |
build() | 每次Widget需要重建时 | 构建UI并返回Widget |
didUpdateWidget() | 父Widget重新构建并传入新参数时调用 | 处理父组件属性变化 |
setState() | 状态变化时手动调用 | 更新Widget并触发重建 |
deactivate() | State从树中暂时移除时 | 清理临时状态(不常用) |
dispose() | State永久移除时 | 释放资源,避免内存泄漏 |
4.生命周期函数的验证
1.创建和销毁时期函数的验证
我们以下面的代码为例,当app启动的之后:
图1.示例demo
控制台打印信息如下:
图2.进入页面控制台打印日志
当我们点击remove按钮之后,移除子控件,控制台的打印信息如下:
图3.点击Remove之后控制台打印日志
2.didUpdateWidget函数验证
要确保 didUpdateWidget 函数被调用,需要使 StatefulWidget 的 父级组件对其属性进行更新,而不是使用 UniqueKey 强制组件重新创建。我们修改下代码,点击按钮之后,修改子组件的颜色,通过更新 ChildWidget 的 color 属性触发 didUpdateWidget 的调用。
效果图如下:
图4.didUpdateWidge函数验证demo
然后我们在didUpdateWidge中打印该函数,点击按钮之后,控制台打印信息如下:
图5.控制台打印日志
完整代码如下:
import 'package:flutter/material.dart';class LifecycleDemoApp extends StatelessWidget {const LifecycleDemoApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(home: const ParentPage(),);}
}class ParentPage extends StatefulWidget {const ParentPage({super.key});@overrideState<ParentPage> createState() => _ParentPageState();
}class _ParentPageState extends State<ParentPage> {Color childColor = Colors.blue;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Lifecycle Demo')),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [ElevatedButton(onPressed: () {setState(() {// Toggle color between blue and greenchildColor = childColor == Colors.blue ? Colors.green : Colors.blue;});},child: const Text('Change Child Widget Color'),),const SizedBox(height: 20),ChildWidget(color: childColor, // Pass updated color),],),));}
}class ChildWidget extends StatefulWidget {final Color color;const ChildWidget({super.key, required this.color});@overrideState<ChildWidget> createState() => _ChildWidgetState();
}class _ChildWidgetState extends State<ChildWidget> {@overridevoid initState() {super.initState();debugPrint("ChildWidget: initState");}@overridevoid didChangeDependencies() {super.didChangeDependencies();debugPrint("ChildWidget: didChangeDependencies");}@overridevoid didUpdateWidget(covariant ChildWidget oldWidget) {super.didUpdateWidget(oldWidget);debugPrint("ChildWidget: didUpdateWidget - Old color: ${oldWidget.color}, New color: ${widget.color}");}@overrideWidget build(BuildContext context) {debugPrint("ChildWidget: build");return Container(height: 100,width: 100,color: widget.color,);}@overridevoid deactivate() {super.deactivate();debugPrint("ChildWidget: deactivate");}@overridevoid dispose() {super.dispose();debugPrint("ChildWidget: dispose");}
}
3.didChangeDependencies函数验证
要验证 didChangeDependencies 的调用时机,我们需要引入一个可以触发依赖改变的机制,比如 InheritedWidget 或 MediaQuery 的更新。以下是代码的更新版本,通过使用 InheritedWidget 来测试 didChangeDependencies 的调用。
我们修改下代码:
import 'package:flutter/material.dart';void main() {runApp(const LifecycleDemoApp());
}class LifecycleDemoApp extends StatelessWidget {const LifecycleDemoApp({super.key});@overrideWidget build(BuildContext context) {return InheritedColorProvider(color: Colors.blue,child: MaterialApp(home: const ParentPage(),),);}
}class InheritedColorProvider extends InheritedWidget {final Color color;const InheritedColorProvider({super.key,required this.color,required Widget child,}) : super(child: child);static InheritedColorProvider? of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<InheritedColorProvider>();}@overridebool updateShouldNotify(InheritedColorProvider oldWidget) {return color != oldWidget.color;}
}class ParentPage extends StatefulWidget {const ParentPage({super.key});@overrideState<ParentPage> createState() => _ParentPageState();
}class _ParentPageState extends State<ParentPage> {Color appColor = Colors.blue;void _changeColor() {setState(() {appColor = appColor == Colors.blue ? Colors.green : Colors.blue;});}@overrideWidget build(BuildContext context) {return InheritedColorProvider(color: appColor,child: Scaffold(appBar: AppBar(title: const Text('Lifecycle Demo'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,children: [ElevatedButton(onPressed: _changeColor,child: const Text('Change Inherited Color'),),const SizedBox(height: 20),const ChildWidget(),],),),);}
}class ChildWidget extends StatefulWidget {const ChildWidget({super.key});@overrideState<ChildWidget> createState() => _ChildWidgetState();
}class _ChildWidgetState extends State<ChildWidget> {@overridevoid initState() {super.initState();debugPrint("ChildWidget: initState");}@overridevoid didChangeDependencies() {super.didChangeDependencies();debugPrint("ChildWidget: didChangeDependencies - Color: ${InheritedColorProvider.of(context)?.color}");}@overrideWidget build(BuildContext context) {debugPrint("ChildWidget: build");final color = InheritedColorProvider.of(context)?.color ?? Colors.transparent;return Container(height: 100,width: 100,color: color,);}@overridevoid didUpdateWidget(covariant ChildWidget oldWidget) {super.didUpdateWidget(oldWidget);debugPrint("ChildWidget: didUpdateWidget");}@overridevoid deactivate() {super.deactivate();debugPrint("ChildWidget: deactivate");}@overridevoid dispose() {super.dispose();debugPrint("ChildWidget: dispose");}
}
在修改之后的代码中,我们做了以下工作:
1.引入了InheritedWidget
InheritedColorProvider 是一个简单的 InheritedWidget,用来共享 color 属性。
2.修改了更新逻辑
在 ParentPage 中,通过修改 InheritedColorProvider 的 color 属性触发 didChangeDependencies。
3.验证依赖关系
子组件 ChildWidget 会依赖 InheritedColorProvider,当 color 发生变化时,didChangeDependencies 会被调用。
当我们点击修改按钮之后,控制台打印日志如下:
ChildWidget: initState
ChildWidget: didChangeDependencies - Color: Color(0xff0000ff)
ChildWidget: build
ChildWidget: didChangeDependencies - Color: Color(0xff00ff00)
ChildWidget: build
相关文章:

深入理解Flutter生命周期函数之StatefulWidget(一)
目录 前言 1.为什么需要生命周期函数 2.开发过程中常用的生命周期函数 1.initState() 2.didChangeDependencies() 3.build() 4.didUpdateWidget() 5.setState() 6.deactivate() 7.dispose() 3.Flutter生命周期总结 1.调用顺序 2.函数调用时机以及主要作用 4.生…...

413: Quick Sort
解法: #include <bits/stdc.h> using namespace std; const int N1e55; int a[N]; int n;int main(int argc, char** argv) {cin>>n;for (int i0;i<n;i) cin>>a[i];sort(a,an);for (int i0;i<n;i) cout<<a[i]<<" "…...
vue之axios根据某个接口创建实例,并设置headers和超时时间,捕捉异常
import axiosNew from axios;//给axios起个别名//创建常量实例 const instanceNew axiosNew.create({//axios中请求配置有baseURL选项,表示请求URL的公共部分,url baseUrl requestUrlbaseURL: baseURL,//设置超时时间为20秒timeout: 20000,headers: {…...
Pandas数据透视表:交叉分析与聚合计算
大家好,在数据分析中,数据透视表(Pivot Table)是一种强大的工具,用于交叉分析和聚合计算。Pandas库中的数据透视表功能,使我们能够在多维数据中快速生成汇总表、统计特定维度的聚合数据,帮助揭示…...
软件设计师考试大纲
文章目录 一 、考 试 说 明1. 考试目标2. 考试要求3. 考试科目设置 二、考 试 范 围考试科目1:计算机与软件工程知识1. 计算机系统基础知识1.1计算机内数据的表示及运算1.2 其他数学基础知识1.3 计算机硬件基础知识1.3.1 计算机系统的组成、体系结构分类及特性1.3.2 存储系统1.…...
一文说清C++类型转换操作符(cast operator)
一 前言 大家在编程时,一定会遇到要做类型转换的应用场景。 但是,C风格的类型转换太强大,太危险,它允许将一个给定类型转换成我们想要的任何其他类型。 所以在C中,提供了一些更安全和更明确的类型转换操作符ÿ…...

MOSFET电路栅源极GS之间并联电容后,MOS炸管原因分析
1、前言 在介绍,在进行MOSFET相关的电路设计时,可能会遇到MOSFET误导通的问题,为了解决此问题,我们提出了两种方法,一种是增大MOSFET栅极串联电阻的阻值,另外一种是在MOSFET栅-源极之间并联一个电容&#…...
gitHub常用操作
gitHub常用操作 1、把项目拉下来2、添加上游仓库3、进入分支4、从上游仓库拉取更新 1、把项目拉下来 在对应项目的右上角点击fork,fork下来:将远程仓库复制到个人仓库 在创建好的分支文件夹下使用 git clone自己远程仓库下的http地址(fork…...

[项目代码] YOLOv5 铁路工人安全帽安全背心识别 [目标检测]
YOLOv5是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv5具有更高的…...

Java 垃圾回收机制(GC)概览
简介 Java垃圾收集、堆和运行时编译器默认选择 jdk1.9开始,默认使用G1收集器,GC Threads的最大数量受堆大小和可用CPU资源限制初始堆大小为物理内存的1/64最大堆大小为物理内存的1/4分层编译器,同时使用C1和C2 JVM 垃圾收集器可以为配置优…...

Kafka节点服役和退役
1 服役新节点 1)新节点准备 (1)关闭 bigdata03,进行一个快照,并右键执行克隆操作。 (2)开启 bigdata04,并修改 IP 地址。 vi /etc/sysconfig/network-scripts/ifcfg-ens33修改完…...
Git如何简单使用
文章目录 GitGitlabGitLab和GitHub有什么区别?Gitlab简单使用Gitlab常用指令Git Git是一个分布式版本控制系统。 它用于记录文件的修改历史,方便多人协作开发软件等项目。例如一个软件开发团队,成员们会频繁修改代码,Git可以追踪每个人的修改内容、时间等信息。 主要功能…...

酒水分销积分商城小程序开发方案php+uniapp
酒水分销积分商城小程序开发,开发语言后端php,前端uniapp。核心功能模块:酒水商城、积分商城、二级分销、抽奖、优惠券。可以二开或定制。协助部署搭建。...
MTU-内核态(数据链路层或网络接口上能够传输的最大数据包大小)
MTU(最大传输单元,Maximum Transmission Unit)是网络中用于表示数据链路层或网络接口上能够传输的最大数据包大小。 1. 工作原理 MTU 决定了一个数据包(包括头部和数据部分)的最大长度。它影响到数据的传输ÿ…...
React的基础API介绍(一)
目录 useEffect1. 替代生命周期方法2. 副作用管理3. 依赖项数组4. 多次使用5. 与闭包配合6. 支持异步操作7. 减少样板代码 注意事项useEffetct是如何拿到变量count最新的值?1. 每次渲染都会创建新的函数作用域2. 闭包捕获最新的状态值3. useEffect 的执行时机 useLa…...
【Electron】总结:如何创建Electron+Element Plus的项目
我将结合官网手册与AI问到的信息,直接给出步骤,与命令。 一、准备环境 首先在C盘Users,你的登录的账号名文件夹下,编辑.npmrc文件。添加镜像地址。 如果使用了yarn,则是.yarnrc。可以全部都配置。 npm install -g …...

从依托指标字典到 NoETL 自动化指标平台,指标口径一致性管理的进阶
今天,我们一起来梳理和盘点下不同代际指标平台如何实现指标口径一致性管理: 第一代:指标口径登记与管理 第一代指标平台聚焦于指标口径的登记与管理,依托指标字典实现企业指标口径的有效检索与管理功能。 此阶段,业…...
嵌入式面试题练习 - 2024/11/15
欢迎找我进行职业规划,超值的自我投资 -> 嵌入式软件工程师一对一指导 1.设有定义char *p[]{"Shanghai","Beijing","Honkong"};则结果为j字符的表达式是() A *p[1] 3 B *(p[1] 3) C *(p[3] 1) D p[3] […...

分析http话术异常挂断原因
用户反馈在与机器人通话时,自己明明有说话,但是通话还是被挂断了,想知道原因。 分析日志 我们根据用户提供的freeswitch日志分析:发现是因为超时导致话术执行hangup动作,结束了通话。 从这一行向上分析日志ÿ…...

云岚到家 秒杀抢购
目录 秒杀抢购业务特点 常用技术方案 抢券 抢券界面 进行抢券 我的优惠券列表 活动查询 系统设计 活动查询分析 活动查询界面显示了哪些数据? 面向高并发如何提高活动查询性能? 如何保证缓存一致性? 数据流 Redis数据结构设计 如…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...