状态管理的方法的使用逻辑
Dart
1.构造函数
构造函数 只适合初始化不依赖 widget 的成员变量
不要在构造函数中访问widget或context!
2.initState()
必须在这里:初始化依赖widget属性的状态、开启监听、初始化控制器
3.didChangeDependencies()
必须在这里处理InheritedWidget变化、初始化依赖BuildContext的逻辑
4.build()
必须在这里返回Widget树
5.didUpdateWidget()
必须在这里响应widget属性变化
6.deactivate()
必须在这里:准备组件从树中移除
7.dispose()
必须在这里:释放所有资源
效果图

Flutter组件建立的事件线
Dart
1. 构造函数:创建 State 对象
2. initState:State 初始化,但 Widget 树还没完全建立
3. 组件被插入 Widget 树,获得真正的 BuildContext
4. didChangeDependencies:通知组件"你现在有完整的 context 了"
5. build:第一次构建 UI
关键点
1.监听应用的生命周期
Dart
//监听应用的生命周期
//这是向 Flutter 引擎注册一个观察者,用来监听整个应用的生命周期事件。
WidgetsBinding.instance.addObserver(this);
//现在这个 State 对象可以接收生命周期事件了
为什么需要它?
Dart
//监听应用状态变化
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
print('应用状态改变: $state');
switch (state) {
case AppLifecycleState.resumed:
// 应用回到前台
_resumeTimer();
break;
case AppLifecycleState.inactive:
// 应用不活跃(如来电、分屏)
break;
case AppLifecycleState.paused:
// 应用进入后台
_pauseTimer();
break;
case AppLifecycleState.detached:
// 应用被销毁
break;
case AppLifecycleState.hidden:
// 应用被隐藏(某些平台)
break;
}
}
//监听系统变化
@override
void didChangeMetrics() {
// 屏幕尺寸改变(旋转、分屏、键盘弹出)
print('屏幕尺寸改变: ${WidgetsBinding.instance.window.physicalSize}');
}
@override
void didChangeTextScaleFactor() {
// 系统字体大小改变
print('文字缩放比例改变');
}
@override
void didChangePlatformBrightness() {
// 系统主题模式改变(亮色/暗色)
setState(() {
_isDarkMode = WidgetsBinding.instance.window.platformBrightness == Brightness.dark;
});
}
2.InheritedWidget
InheritedWidget 就像一个"共享公告板",所有子组件都能从它那里读取数据。
例子
Dart
// 这些都是 InheritedWidget:
Theme.of(context) // 获取主题
MediaQuery.of(context) // 获取屏幕信息
Localizations.of(context) // 获取本地化文本
Navigator.of(context) // 导航器
3.BuildContext()
Dart
// 每个 Widget 都有自己的 BuildContext
// 就像每个人都有自己的家庭地址
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) { // <- 这个 context 就是地址
return Container();
}
}
4.什么情况下didChangeDependencies()会调用
当公告板内容变化时,Flutter 会通知所有"订阅了公告板"的组件:
Dart
情况1:主题变化
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 当用户切换亮色/暗色主题时,这里会被调用
final brightness = MediaQuery.of(context).platformBrightness;
print('主题变了:$brightness');
}
情况2:屏幕旋转
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 屏幕旋转时,MediaQuery 数据变化
final size = MediaQuery.of(context).size;
print('屏幕尺寸变了:${size.width}x${size.height}');
}
情况3:语言切换
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 当用户切换App语言时
final locale = Localizations.localeOf(context);
print('语言变了:$locale');
}
5.什么情况下didUpdateWidget()会调用
Dart
父组件重新构建(比如父组件的 setState())
给子组件传递了新的属性值
widget 的 type 和 key 都没变(还是同一个组件实例)
组件没有被销毁(还在 widget 树中)
比喻:
老板(父组件)说:"小张(子组件),你还继续做这个工作"
"但工作内容更新了,这是新要求(新属性)"
你就需要响应这个更新
举例:父组件重新构建并传入新属性
Dart
// 父组件
class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
String _title = "旧标题";
Color _color = Colors.blue;
@override
Widget build(BuildContext context) {
return Column(
children: [
// 子组件
ChildWidget(
title: _title, // 属性1
color: _color, // 属性2
),
ElevatedButton(
onPressed: () {
setState(() {
_title = "新标题"; // ← 改变属性!
});
},
child: Text('改标题'),
),
],
);
}
}
这个例子的触发过程
Dart
1. 点击按钮 → setState()
2. 父组件重新构建
3. 给 ChildWidget 传入新的 title
4. Flutter 发现:还是同一个 ChildWidget
5. 但属性变了 → 调用 didUpdateWidget()
6.什么情况下deactivate()会调用
Dart
比喻:deactivate() 就是组件说:"我要暂时离开舞台了,但可能还会回来!"
情况1:页面跳转
// 当前页面 A,跳转到页面 B
Navigator.push(context, MaterialPageRoute(builder: (context) => PageB()));
// 页面 A 会调用 deactivate()
// 但页面 A 还在导航堆栈中,可能还会回来
情况2:组件被暂时移除
// 条件渲染
if (showWidget) {
MyWidget() // 当 showWidget 从 true 变 false 时
}
// MyWidget 会调用 deactivate()
// 但组件对象还在内存中
情况3:在列表中移动位置
// AnimatedList、ReorderableListView 中移动项目
// 项目暂时从树中移除,然后插入新位置
代码实例
Dart
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<StatefulWidget> createState() => _HomePageState();
}
// ====================演示用的可复用组件---显示计数器的矩形=============================
class CounterDisplay extends StatelessWidget {
final int count;
final Color color;
const CounterDisplay({super.key, required this.count, required this.color});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
border: Border.all(color: color, width: 2),
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('计数器显示:'),
const SizedBox(height: 10),
Text(
'$count',
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
color: color,
),
),
],
),
);
}
}
class _HomePageState extends State<HomePage>
with WidgetsBindingObserver { //用于监听应用生命周期
//创建阶段:构造函数 只适合初始化不依赖 widget 的成员变量
//不要在构造函数中访问widget或context!
_HomePageState(){
print("构造函数_HomePageState()被调用");
print("此时widget属性还未关联,不能访问widget属性");
}
//内部状态
int _counter = 0; //计数器
Timer? _timer; //定时器
bool _isDarkMode = false; //主题模式的标志
DateTime? _lastUpdateTime; //最后更新时间
StreamSubscription<int>? _streamSubscription; //订阅
Color _counterColor = Colors.blue;
//创建阶段:initState()
//必须在这里:初始化依赖widget属性的状态、开启监听、初始化控制器
@override
void initState() {
super.initState();
print("2. initState()被调用");
print("widget属性已关联,可以访问:${widget.runtimeType}"); //runtimeType 是 Dart 中所有对象都有的一个属性,它返回对象的实际运行时类型。 这里显示的是HomePage
//1.初始化需要widget属性的状态
_counterColor = _isDarkMode ? Colors.amber : Colors.blue;
//2.添加WidgetBinding观察者(监听应用生命周期)
WidgetsBinding.instance.addObserver(this);
print("添加应用生命周期监听");
//3.启动一个计时器
_startTimer();
//4.监听一个stream
_setupStreamListener();
//5.模拟从共享首选项加载数据
_loadInitialData();
}
//创建阶段:didChangeDependencies()
//必须在这里处理InheritedWidget变化、初始化依赖BuildContext的逻辑
@override
void didChangeDependencies(){
super.didChangeDependencies();
print("3. didChangeDependencies()被调用");
//1.处理InheritedWidget的变化
//例如:MediaQuery、Theme、Localizations等
final currentBrightness = MediaQuery.of(context).platformBrightness;
print("检测到平台亮度:$currentBrightness");
//2.初始化依赖context的逻辑
//例如:检查路由参数
final routeArgs = ModalRoute.of(context)?.settings.arguments;
if (routeArgs != null) {
print('获取到路由参数: $routeArgs');
}
}
//创建阶段:build()
//必须在这里返回Widget树
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('生命周期完整演示'),
actions: [
Switch(value: _isDarkMode, onChanged: _toggleTheme),
],
),
body:Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 当前状态显示
_buildStatusDisplay(),
const SizedBox(height: 20),
// 控制按钮
_buildControlButtons(),
const SizedBox(height: 20),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: '增加计数器',
child: const Icon(Icons.add),
),
);
}
//更新阶段:didUpdateWidget()
//必须在这里响应widget属性变化
@override
void didUpdateWidget(covariant HomePage oldWidget) {
super.didUpdateWidget(oldWidget);
print('5. didUpdateWidget() 被调用');
print('旧的widget: ${oldWidget.runtimeType}, 新的widget: ${widget.runtimeType}');
// 1. 当父组件重建此widget并传入新属性时调用
// 2. 比较新旧widget的属性,决定是否需要更新状态
// 3. 这里可以添加逻辑来处理widget属性的变化
_lastUpdateTime = DateTime.now();
}
//销毁阶段:deactivate()
//必须在这里:准备组件从树中移除
@override
void deactivate() {
print('6. deactivate() 被调用');
print('组件即将从Widget树中移除,但可能被重新插入');
// 1. 当组件从树中移除时调用
// 2. 组件可能被暂时移除然后重新插入
// 3. 这里应该清理临时资源,但保留可以恢复的状态
super.deactivate();
}
//销毁阶段:dispose()
// 必须在这里:释放所有资源
@override
void dispose() {
print('dispose() 被调用');
print('组件永久销毁,必须清理所有资源');
// 1. 取消计时器
_timer?.cancel();
print('计时器已取消');
// 2. 取消Stream订阅
_streamSubscription?.cancel();
print('Stream订阅已取消');
// 3. 移除WidgetsBinding观察者
WidgetsBinding.instance.removeObserver(this);
print('应用生命周期监听已移除');
// 4. 清理控制器、动画等
print('所有资源已清理');
super.dispose();
}
// 应用生命周期回调
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
print('应用状态变化: $state');
switch (state) {
case AppLifecycleState.resumed:
print('应用回到前台');
// 恢复定时器、重新连接网络等
_timer?.isActive ?? _startTimer(); // 如果定时器停了就重启
break;
case AppLifecycleState.paused:
print('应用进入后台');
break;
case AppLifecycleState.inactive:
print('应用不活跃(如来电、分屏)');
break;
case AppLifecycleState.detached:
print('应用即将被销毁');
break;
case AppLifecycleState.hidden:
print('应用被隐藏');
break;
}
}
//=================================计时器===================================
void _startTimer(){
int seconds = 0;
//Timer.periodic - 创建周期性计时器,一秒执行一次
_timer = Timer.periodic(const Duration(seconds: 1), (timer){
if(!mounted){ //如果组件已卸载,停止计时器
timer.cancel();
return;
}
//更新UI,
setState(() {
seconds = timer.tick;// timer.tick 是计时器已经触发的次数(从1开始)
});
print("计时器运行:$seconds 秒");
});
}
//===================================监听======================================
void _setupStreamListener(){
// 如果已有订阅,先取消
_streamSubscription?.cancel();
//模拟一个每秒发射数据的stream
//Stream.periodic - 创建一个周期性产生数据的Stream
final stream = Stream.periodic(
const Duration(seconds: 2), //每两秒产生一次数据
(count) => count * 10, //生成每次返回的数据
).take(20); //只取前20个数据
//订阅这个Stream,开始监听数据
_streamSubscription = stream.listen((data){
print("Stream数据接收:$data");
},onDone: (){ //当20次完成了之后,就执行以下代码
//Stream完成时的回调函数
print("Stream已完成");
});
}
//=============================异步数据加载过程==============================
Future<void> _loadInitialData() async{
print("模拟加载初始数据...");
await Future.delayed(const Duration(milliseconds: 500));
print("初始数据加载完成");
}
//================================增加计数器=================================
void _incrementCounter(){
setState(() {
_counter++;
_counterColor = _counter % 3 == 0
? Colors.red
: _counter % 2 == 0
? Colors.green : Colors.blue;
});
print("计数器增加到:$_counter");
}
//================================重置计数器=================================
void _resetCounter(){
setState(() {
_counter = 0;
_counterColor = Colors.blue;
});
print("计数器已重置");
}
//====================================切换主题按钮=============================
void _toggleTheme(bool value){
setState(() {
_isDarkMode = value;
});
print("主题切换为:${_isDarkMode ? "深色" : "浅色"}");
}
void _simulateWidgetUpdate() {
print('模拟父组件更新widget属性...');
// 在实际应用中,父组件会重建此widget
}
//=================================跳转页面==============================
void _navigateAndReturn() async {
print('跳转到新页面...');
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SimplePage(),
),
);
print('从新页面返回: $result');
}
//==================================当前状态UI =================================
Widget _buildStatusDisplay() {
return Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'当前状态:',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Row(
children: [
//显示的蓝色圆角矩形
CounterDisplay(count: _counter, color: _counterColor),
const SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('计数器: $_counter'),
Text('主题: ${_isDarkMode ? '深色' : '浅色'}'),
if (_lastUpdateTime != null)
Text('最后更新: ${_lastUpdateTime!.toString().substring(11, 19)}'),
Text('组件状态: ${mounted ? '已挂载' : '未挂载'}'),
],
),
),
],
),
],
),
),
);
}
//=================================控制按钮==============================
Widget _buildControlButtons() {
return Wrap(
spacing: 10,
runSpacing: 10,
children: [
ElevatedButton(
onPressed: _incrementCounter,
child: const Text('增加计数器'),
),
ElevatedButton(
onPressed: _resetCounter,
child: const Text('重置计数器'),
),
ElevatedButton(
onPressed: _simulateWidgetUpdate,
child: const Text('模拟Widget更新'),
),
ElevatedButton(
onPressed: _navigateAndReturn,
child: const Text('跳转页面'),
),
ElevatedButton(
onPressed: () {
print('手动添加日志');
},
child: const Text('添加日志'),
),
],
);
}
}
//=====================================用于跳转的简单页面==========================
class SimplePage extends StatelessWidget {
const SimplePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('新页面'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('点击返回查看deactivate效果'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context, '返回数据');
},
child: const Text('返回'),
),
],
),
),
);
}
}