生命周期
理解 StatefulWidget 的生命周期,是写出正确、高效 Flutter 代码的关键。
生命周期流程图
┌──────────────┐
创建 Widget │ createState │
────────────► │ │
└──────┬───────┘
│
▼
┌──────────────┐
│ initState │ ← 只调用一次
│ (初始化) │
└──────┬───────┘
│
▼
┌──────────────────┐
│didChangeDependencies│ ← 依赖变化时
└──────┬───────────┘
│
┌────────────┴────────────┐
▼ ▼
┌────────────┐ ┌─────────────┐
│ build │◄─────────│ didUpdateWidget │
│ (构建UI) │ 属性变化 │ (属性更新时) │
└──────┬─────┘ └─────────────┘
│
▼
┌────────────┐ setState()
│ build │◄──────────
│ (重建UI) │ 状态变化
└──────┬─────┘
│
▼
┌────────────┐
│ deactivate │ ← 从树中移除(可能被重新插入)
└──────┬─────┘
│
被丢弃? ── 是 ──► ┌──────────┐
│ │ dispose │ ← 永久销毁
否 └──────────┘
│
└─── 重新插入树 ──► 回到 build()各阶段详解
initState()
dart
@override
void initState() {
super.initState();
// ✅ 在这里做初始化工作
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_loadData();
}适用场景:
- 初始化控制器(AnimationController、TextEditingController、ScrollController)
- 初始化状态变量的初始值
- 订阅事件流(Stream)
- 发起首次网络请求
- 添加监听器
注意:
- 不能在这里调用
BuildContext相关方法(如Theme.of(context)),此时 context 还未完全就绪 - 需要 context 的初始化操作请放在
didChangeDependencies()中
didChangeDependencies()
dart
@override
void didChangeDependencies() {
super.didChangeDependencies();
// ✅ 在这里使用 context 依赖的值
final size = MediaQuery.of(context).size;
final theme = Theme.of(context);
}适用场景:
- 获取
MediaQuery、Theme、InheritedWidget的值 - 当这些依赖发生变化时会重新调用
build()
dart
@override
Widget build(BuildContext context) {
return Container(
child: Text('count: $_count'),
);
}适用场景:
- 根据当前状态构建 UI
- 每次
setState()后都会被调用 - 尽量保持纯函数,避免副作用
didUpdateWidget()
dart
@override
void didUpdateWidget(covariant MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// ✅ 父 Widget 传入新属性时的处理
if (widget.value != oldWidget.value) {
_controller.animateTo(widget.value);
}
}适用场景:
- 当父组件传入的属性变化时,同步更新内部状态
- 比较新旧属性
widget.xxx != oldWidget.xxx
deactivate()
dart
@override
void deactivate() {
super.deactivate();
// 通常不需要做什么
}说明:
- 当 State 对象从 Widget 树中移除时调用
- 可能会被重新插入(比如从一个位置移到另一个位置)
- 通常不需要重写这个方法
dispose()
dart
@override
void dispose() {
// ✅ 必须释放所有资源!
_controller.dispose();
_textEditingController.dispose();
_scrollController.dispose();
_subscription.cancel();
super.dispose();
}适用场景:
- 释放所有控制器(AnimationController、TextEditingController 等)
- 取消订阅(StreamSubscription、ChangeNotifier)
- 取消定时器
- 移除监听器
重要
忘记调用 dispose() 会导致内存泄漏!只要在 initState() 中创建/订阅了资源,就必须在 dispose() 中释放/取消。
常见模式
带网络请求的 Widget
dart
class UserList extends StatefulWidget {
const UserList({super.key});
@override
State<UserList> createState() => _UserListState();
}
class _UserListState extends State<UserList> {
List<User> _users = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
_fetchUsers(); // 初始化时加载数据
}
Future<void> _fetchUsers() async {
try {
final users = await ApiService.getUsers();
// ─── ★ 异步操作后检查 mounted ──────────────
if (!mounted) return;
// ─── ☆ 异步操作后检查 mounted ──────────────
setState(() {
_users = users;
_isLoading = false;
});
} catch (e) {
if (!mounted) return;
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return const CircularProgressIndicator();
}
return ListView.builder(
itemCount: _users.length,
itemBuilder: (context, index) {
return ListTile(title: Text(_users[index].name));
},
);
}
}带动画的 Widget
dart
class FadeIn extends StatefulWidget {
const FadeIn({super.key, required this.child});
final Widget child;
@override
State<FadeIn> createState() => _FadeInState();
}
class _FadeInState extends State<FadeIn> with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this, // SingleTickerProviderStateMixin 提供 vsync
duration: const Duration(milliseconds: 500),
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
);
_controller.forward(); // 启动动画
}
@override
void dispose() {
_controller.dispose(); // 释放控制器
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: widget.child,
);
}
}mounted 检查
在异步操作后调用 setState 前,检查组件是否还在 Widget 树中:
dart
import 'package:flutter/material.dart';
class MountedCheckPage extends StatefulWidget {
const MountedCheckPage({super.key});
@override
State<MountedCheckPage> createState() => _MountedCheckPageState();
}
class _MountedCheckPageState extends State<MountedCheckPage> {
String _data = '等待加载';
void _fetchData() async {
final result = await Future.delayed(const Duration(seconds: 2));
if (!mounted) return; // 组件已被销毁
setState(() => _data = '结果: $result');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('mounted 检查')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_data, style: const TextStyle(fontSize: 20)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _fetchData,
child: const Text('加载数据'),
),
],
),
),
);
}
}mounted 是 State 类的属性,表示当前 State 是否还在 Widget 树中。在异步回调中修改状态前,务必检查 mounted。
生命周期速查
| 方法 | 调用时机 | 用途 |
|---|---|---|
initState() | 插入树时,仅一次 | 初始化控制器、订阅、网络请求 |
didChangeDependencies() | 依赖变化时 | 获取 MediaQuery、Theme 等 |
build() | 每次需要渲染时 | 构建 UI |
didUpdateWidget() | 父 Widget 重建时 | 响应属性变化 |
deactivate() | 从树中移除时 | 通常不需要 |
dispose() | 永久销毁时 | 释放所有资源 |
