Appearance
flutter-lifecycle-observer
1. 实验目标
用最小 Flutter 页面验证:
initStatedidChangeDependenciesbuilddeactivatedisposeWidgetsBindingObserver.didChangeAppLifecycleState- 页面 push / pop 时状态保留与重建差异
并观察:
- 页面 push / pop 时组件生命周期如何变化
- App 前后台切换时
AppLifecycleState如何变化 build高频触发,但initState/dispose不是一一对应的- 切换
Key后原State如何被替换 - route 返回后哪些状态保留、哪些状态重置
2. 工程要点
StatefulWidget负责页面级状态WidgetsBindingObserver负责监听 App 前后台事件- 页面内维护事件日志,便于静态阅读与运行时对照
- 提供“打开详情页”入口,观察父子页面的创建与销毁顺序
- 提供局部计数器、输入框、强制换 Key,观察 rebuild 与 recreate 的差异
3. 关键实现速览
3.1 生命周期日志 + AppLifecycleState 监听
dart
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_append('initState');
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
_append('AppLifecycleState.$state');
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_appendDirect('dispose');
_controller.dispose();
super.dispose();
}为什么看这段:它把页面 state 生命周期和宿主 App 前后台信号放到同一份日志里,能直接对照“切后台时页面并没有立刻 dispose”。
3.2 触发 rebuild 与触发 recreate 的差异
dart
ElevatedButton(
onPressed: () => setState(() {
_counter += 1;
}),
child: const Text('Trigger rebuild'),
),
ElevatedButton(
onPressed: () => setState(() {
_childVersion += 1;
}),
child: const Text('Replace child with new Key'),
),
StatefulColorChip(key: ValueKey<int>(_childVersion)),为什么看这段:同样都叫“界面变化”,但 setState 只是让当前树重新 build,而换 Key 会让子节点失去复用资格,触发新的 State 生命周期。
3.3 路由返回后状态保留观察
dart
final result = await Navigator.of(context).push<String>(
MaterialPageRoute<String>(
builder: (_) => const _LifecycleDetailPage(),
),
);
if (!mounted) return;
setState(() {
_lastDetailResult = result ?? 'detail returned without result';
});为什么看这段:它能直接观察“当前页通常没有销毁,只是在详情页返回后继续使用原来的 State”。
完整源码: lifecycle_observer_page.dart
4. 运行方式
bash
cd labs/flutter/host
flutter pub get
flutter run -d macos
# 或
flutter run -d ios
# 或
flutter run -d android启动后在首页进入 Flutter Lifecycle Observer。
5. 预期现象
场景 A:首次进入页面
预期顺序:
text
initState
→ didChangeDependencies
→ build #1观察点:
- 日志中
initState只出现一次 - 点击
Trigger rebuild后只新增build,不会再次出现initState
场景 B:打开详情页再返回
预期行为:
- 当前页通常不会立刻
dispose - 详情页会显示自己的进入时间
- 返回后当前页继续保留计数器、输入框内容、日志历史
- 返回结果会写回
Last detail result
观察点:
- 看当前页是否只是追加
build - 看
Counter、文本输入内容是否还在
场景 C:App 切后台再回前台
预期行为:
- 日志追加
AppLifecycleState.inactive/paused/resumed等状态 - 当前页通常不销毁
观察点:
- 切后台时是否没有触发
dispose - 回前台后当前局部状态是否保留
场景 D:替换子节点 Key
预期行为:
- 父页面仍保留自身状态
- 子组件旧
State触发deactivate/dispose - 新子组件重新
initState/build
观察点:
- 对照父日志与子日志,理解 rebuild 与 recreate 的区别
6. 常见误区
| 误区 | 实际情况 |
|---|---|
build 只会执行一次 | 错。依赖变化、父组件重建、返回页面都可能再次执行 |
dispose 一定紧跟 deactivate | 不一定。元素树调整时可能先 deactivate,未必立刻销毁 |
| Flutter 页面生命周期等同 Android Activity 生命周期 | 不等同。Flutter 还有 Widget / Element 层的重建与销毁 |
| 页面返回后内容没丢,说明没发生重建 | 不对。很可能发生了 build,只是 State 被复用 |
| App 切后台一定会销毁当前页面 | 不一定。更多时候只是进入 AppLifecycleState.paused |
7. 对应知识库文档
- 理论主文档:Flutter 生命周期总览