Appearance
Flutter 生命周期总览
Flutter 生命周期不是单一“页面生命周期”,而是 App 生命周期 + Widget/Element 生命周期 + 路由栈变化 共同作用的结果。
代码索引
| 主题 | Lab 说明 | 源码 |
|---|---|---|
| Widget 生命周期与 AppLifecycleState | flutter-lifecycle-observer | lifecycle_observer_page.dart |
为什么需要
Flutter 页面“看起来像一个页面”,但底层并不是 Android Activity 那种单层对象模型。实际开发里经常踩坑:
- 以为
build只会执行一次,结果在返回页面、依赖变化、父组件重建时重复触发 - 以为页面关闭就一定马上
dispose - 以为 Flutter 页面生命周期可以直接等同 Android
Activity生命周期 - 以为 App 退后台只要监听 Widget 生命周期就够了,忽略了
AppLifecycleState
底层机制
1. Widget / Element / RenderObject 是三层概念
Widget:描述“我想长什么样”,是不可变配置Element:Widget 在树中的挂载实例,负责把新旧 Widget 对应起来RenderObject:真正参与布局与绘制
所以 Flutter 生命周期不能只盯着 Widget 类本身,而要理解:
- Widget 会被频繁重建
- Element 可能复用
- State 可能保留,也可能销毁
2. StatefulWidget 的常见生命周期顺序
最常见顺序:
text
initState
→ didChangeDependencies
→ build
→ (多次 build)
→ deactivate
→ dispose但要注意:
build可以执行很多次deactivate不代表一定马上销毁- 只有最终离开树且不再复用时,才会
dispose
3. App 前后台是另一条生命周期线
Flutter 提供:
dart
WidgetsBindingObserver.didChangeAppLifecycleState常见状态:
resumedinactivepauseddetachedhidden(部分平台/版本)
这一层更接近宿主 App 的前后台切换,而不是单个页面的 push/pop。
Android / Flutter / Web / Backend 对照
| 维度 | Flutter | Android | Web | Backend |
|---|---|---|---|---|
| 页面创建 | initState | onCreate | 路由挂载 / 首次 render | 请求进入 handler |
| 页面可见可交互 | build 完成后进入前台交互 | onResume | DOM 可交互 | 请求处理中 |
| 页面离开但可能回退 | 路由栈保留,页面可能仅失焦 | onPause / onStop | 页面被遮挡或切换路由缓存 | 无直接对应 |
| 最终销毁 | dispose | onDestroy | 组件 unmount | 请求结束 / 对象释放 |
| App 前后台 | AppLifecycleState | onStart/onStop + 进程状态 | tab visibility | 进程级守护/无前后台 |
常见场景
1. 列表页进入详情页再返回
- 列表页通常不会立刻销毁
- 返回后往往只是再次
build - 如果你把“昂贵初始化”写在
build里,就会重复执行
对应 Lab:flutter-lifecycle-observer · lifecycle_observer_page.dart
2. App 退后台暂停轮询/动画/定位
这类逻辑不能只靠页面 dispose,因为:
- 页面可能还在栈里
- 但 App 已经退后台
这时要监听 AppLifecycleState.paused/resumed。
3. Provider / Stream / Controller 释放时机判断
若资源跟页面实例强绑定,通常在 dispose 里释放;若资源跟更上层作用域绑定,则不能简单放当前页面 dispose。
常见坑
| 坑 | 现象 | 修法 |
|---|---|---|
在 build 中做网络请求 | 返回页面或父组件刷新时重复请求 | 首次加载放 initState 或受控状态层 |
| 把 App 前后台逻辑写进页面 push/pop | 退后台不生效 | 监听 WidgetsBindingObserver |
误把 deactivate 当最终销毁 | 日志顺序判断错误 | 真正释放放 dispose |
页面销毁后仍回调 setState | 报 setState() called after dispose() | 回调前检查 mounted |
与相近概念对比
| 概念 | 关注点 | 适合处理的问题 |
|---|---|---|
initState | State 首次创建 | 一次性初始化、controller 创建 |
didChangeDependencies | 依赖变动 | 依赖 InheritedWidget 的初始化 |
build | 渲染描述 | 纯 UI 组装 |
deactivate | 暂时离开树 | 很少作为主要业务钩子 |
dispose | 最终释放 | controller / listener / stream 关闭 |
didChangeAppLifecycleState | App 前后台切换 | 暂停轮询、恢复订阅、页面级埋点 |
对应实验
各章节内已嵌入跳转链接;此处汇总全部 Lab:
| Lab | 说明 | 源码 |
|---|---|---|
| flutter-lifecycle-observer | flutter-lifecycle-observer | lifecycle_observer_page.dart |
复习检查题
为什么 Flutter 里不能把页面生命周期简单理解成 Android
Activity生命周期?答:因为 Flutter 还有 Widget / Element / RenderObject 三层;页面 push/pop、父组件重建、依赖变化都会触发 Widget 重建,而不一定对应宿主页面的创建销毁。
为什么
build不适合放一次性初始化逻辑?答:因为
build会多次执行,返回页面、父组件重建、依赖变化都可能触发;一次性初始化更适合放initState或受控状态层。App 退后台暂停任务应该主要监听什么?
答:主要监听
WidgetsBindingObserver.didChangeAppLifecycleState,而不是只依赖页面dispose。
速记
initState:建一次build:可能很多次deactivate:离开树,不一定销毁dispose:最终释放- App 前后台:看
AppLifecycleState