在 Flutter 中,StatefulWidget 的生命周期实际上是由其关联的 State<T> 对象管理的。掌握这些生命周期方法对于正确初始化资源、响应状态变化、避免内存泄漏以及优化渲染性能至关重要。
以下是 StatefulWidget 的完整生命周期解析:
🔁 核心生命周期方法(按典型调用顺序)
| 方法 | 调用时机 | 调用次数 | 主要用途 |
|---|---|---|---|
createState() |
框架首次需要渲染该 Widget 时 | 1次 | 创建 State 实例(定义在 StatefulWidget 类中) |
initState() |
State 首次插入 Widget 树时 |
1次 | 初始化数据、创建控制器、添加监听器等 |
didChangeDependencies() |
依赖的 InheritedWidget 发生变化时;或在 initState() 后立即调用一次 |
多次 | 获取依赖 context 的资源(如 Theme、MediaQuery) |
build() |
首次渲染、setState()、依赖变化或父级重建时 |
多次 | 返回 UI 组件树(必须为纯函数) |
didUpdateWidget(oldWidget) |
父组件重建且传入了新的配置属性时 | 多次 | 同步新旧 Widget 的属性差异 |
setState() |
开发者手动调用 | 多次 | 通知框架状态已变,触发 build() |
deactivate() |
State 从树中临时移除时(可能稍后重新插入) |
多次 | 一般无需重写,处理复杂树重组时可用 |
dispose() |
State 永久从树中移除时 |
1次 | 清理资源:取消监听、释放控制器、停止定时器等 |
📊 典型调用时序
首次创建:
createState() → initState() → didChangeDependencies() → build()
状态更新(开发者触发):
setState() → build()
父级重建 / 属性变化:
didUpdateWidget() → build()
依赖的 InheritedWidget 变化:
didChangeDependencies() → build()
组件从树中移除:
deactivate() → dispose()
💡 注意:
deactivate()和dispose()的区别在于,deactivate()移除后可能 被重新插入树中(如页面切换、GlobalKey 复用),而dispose()是永久销毁。
⚠️ 关键注意事项 & 最佳实践
-
initState中不能直接调用setState()此时 Widget 树尚未完全建立,调用会抛异常。如需首帧后更新状态,可使用:
dartWidgetsBinding.instance.addPostFrameCallback((_) { setState(() { /* ... */ }); }); -
善用
mounted属性在异步操作回调中调用
setState前,务必检查if (mounted),避免组件已销毁后仍尝试更新状态。 -
build()必须是纯函数不应包含副作用(如网络请求、打印日志、修改状态)。只做 UI 描述,保持快速可重复执行。
-
didChangeDependencies()的陷阱它会在
initState后立即调用一次,之后每次依赖变化都会触发。避免在此做耗时操作或重复初始化逻辑。 -
didUpdateWidget()用于属性同步当父组件重建并传入新参数时调用,适合对比
widget.xxx与oldWidget.xxx的差异并更新内部状态。 -
dispose()必须彻底清理忘记释放
AnimationController、StreamSubscription、Timer、FocusNode等是内存泄漏的常见原因。 -
deactivate()日常开发极少重写除非你在实现复杂的 Widget 复用逻辑或自定义路由过渡,否则保持默认即可。
📝 代码结构示例
dart
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key, required this.title});
final String title;
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
@override
void initState() {
super.initState();
// 1. 初始化
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 2. 依赖变化时响应
}
@override
void didUpdateWidget(MyStatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// 3. 属性变化时同步
}
@override
Widget build(BuildContext context) {
// 4. 构建 UI
return Text('${widget.title}: $_counter');
}
@override
void dispose() {
// 5. 清理资源
super.dispose();
}
}
✅ 总结口诀
初始化在
initState,依赖响应在didChangeDependencies,
属性同步看didUpdateWidget,UI 构建交给build,
状态更新用setState,资源释放进dispose。