Skip to content

flutter-lifecycle-observer ​

1. 实验目标 ​

用最小 Flutter 页面验证:

  • initState
  • didChangeDependencies
  • build
  • deactivate
  • dispose
  • WidgetsBindingObserver.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. 对应知识库文档 ​

8. 关键源码 ​

站点构建时间:2026/8/24 23:43:17