Skip to content

Flutter 生命周期总览 ​

Flutter 生命周期的核心不是“一个页面从创建到销毁”这么简单,而是 宿主 App 前后台状态 + Route 栈变化 + Widget / Element / RenderObject 三棵树的重建与释放 共同决定页面行为。

一句话定义 ​

Flutter 生命周期关注的是:配置对象何时重建、挂载实例何时复用、渲染对象何时参与布局绘制、页面状态何时保留,以及宿主 App 切前后台时 Flutter 层会收到什么信号。

代码索引 ​

主题Lab 说明源码
Widget 生命周期、AppLifecycleState、路由 push/pop 观察flutter-lifecycle-observerlifecycle_observer_page.dart

本篇覆盖声明 ​

本文聚焦 Flutter 生命周期主线,重点回答 5 个高频问题:

  1. Widget / Element / RenderObject 分别是什么,为什么 Android 背景工程师容易把它们混成“一个 View”
    • 一句话答:Widget 是不可变配置,Element 是挂载与复用节点,RenderObject 才是渲染对象;Android 倾向将这三者统合在单个 View 实例中。(详见下文 §1及复习题 1)
  2. initState / didChangeDependencies / build / deactivate / dispose 的执行顺序、适合放什么、不适合放什么
    • 一句话答:按一次性初始化(initState) -> 依赖计算(didChangeDependencies) -> UI描述(build) -> 暂离树(deactivate) -> 销毁(dispose) 顺序执行;一次性副作用切忌塞入 build。(详见下文 §2及复习题 2)
  3. AppLifecycleState 与页面 push/pop 不是一回事,分别该解决什么问题
    • 一句话答:AppLifecycleState 解决应用级切前后台的资源暂停与恢复,push/pop 解决路由栈导航与页面曝光刷新。(详见下文 §3及复习题 3)
  4. 页面重建时哪些状态会保留,哪些状态会丢,怎么做状态保存与恢复
    • 一句话答:同位置同 Key 的 Element/State 在 rebuild 时保留,节点移除或 Key 变化则丢掉;跨配置变更与回收需借助 PageStorage 或 RestorationMixin。(详见下文 §4及复习题 4)
  5. Flutter 生命周期与 Android / iOS 宿主生命周期怎么映射,什么地方只能类比、不能硬等价
    • 一句话答:宿主 Activity/ViewController 管原生存活与 AppLifecycleState,Flutter Route/State 管内部页面与组件,宿主后台不等于内部 Route 立即 dispose。(详见下文 §5及复习题 5)

Android / Flutter / Dart 是主线;Web / Backend 仅保留帮助理解边界的必要对照。

推荐学习顺序 ​

  1. 先看 Widget / Element / RenderObject 三层模型,建立“Flutter 不等于单层 View 树”的认知
  2. 再看 StatefulWidget 生命周期顺序,理解 initState / build / dispose 的职责边界
  3. 再看 AppLifecycleState,区分“页面切换”和“App 退后台”
  4. 再看页面重建与状态保存,回答“为什么页面回来没销毁但又重建了”
  5. 最后看 Flutter ↔ 原生宿主生命周期映射表,建立跨端对照

为什么需要 ​

Flutter 页面“看起来像一个页面”,但底层并不是 Android Activity 那种单层对象模型。真实工程里高频踩坑基本都来自这个误判:

  • 为什么 build 会被频繁重跑,而不是只执行一次?
    • 一句话答:build 只是生成不可变 Widget 配置快照,父节点 setState、InheritedWidget 依赖更新或路由恢复均会触发 UI 重述。(详见下文 §1、§2)
  • 为什么页面从栈顶离开不一定马上触发 dispose?
    • 一句话答:若页面被压入栈下层或使用缓存(如 KeepAlive),其 State 节点依然存活在 Element 树中。(详见下文 §2、§3)
  • 为什么 Flutter 页面生命周期不能直接映射成 Android Activity 生命周期?
    • 一句话答:Activity 是系统级组件与进程载体,而 Flutter 页面只是单一宿主 Activity 内部组件树上的 Route 快照。(详见下文 §5及复习题 5)
  • 为什么 App 退后台时只等 dispose 无法暂停资源?
    • 一句话答:切后台时栈中 Route 依然存活不会触发 dispose,必须监听 AppLifecycleState 暂停前台任务。(详见下文 §3及复习题 3)
  • 为什么状态放在 State 字段里在热重建或宿主回收后会丢失?
    • 一句话答:State 只是内存变量,当 Element 被销毁重建或进程被杀死恢复时内存重建,需配合状态恢复机制。(详见下文 §4及复习题 4)

对 Android 背景工程师来说,最大的迁移点是:

  • Android 常把 Activity / Fragment 当“页面实例”主载体
  • Flutter 则把“配置描述”“挂载实例”“渲染对象”拆成三层
  • 所以你必须先判断自己面对的是 重建配置、复用挂载节点,还是 真正释放页面状态

底层机制 ​

1. Widget / Element / RenderObject:Flutter 为什么不是单层 View 树 ​

Widget、Element、RenderObject 是 Flutter UI 体系里三个职责完全不同的对象:

层一句话职责更像 Android 里的什么关键特点
Widget不可变配置,描述“我想长什么样”View 的 XML / Compose 参数快照轻量、可频繁重建
ElementWidget 在树中的挂载实例,负责把新旧 Widget 对应起来介于 View 实例与组合树节点之间持有 BuildContext,决定是否复用 State
RenderObject真正负责 layout / paint / hit testAndroid View 的测量绘制能力只在需要布局绘制的节点上存在

关键理解:

  • Widget 可以频繁 new:Flutter 鼓励你把 build 当纯函数式描述,不要害怕 Widget 重建
  • Element 才决定复用关系:同一个位置、同一个 runtimeType、同一个 key,Flutter 会尝试复用旧 Element / State
  • RenderObject 关注布局和绘制:不是每个 Widget 都直接对应一个新的 RenderObject

可以把一次更新粗略理解成:

text
父级 setState
→ 重新执行 build
→ 产生新的 Widget 配置树
→ Element 对比新旧 Widget
→ 能复用则更新引用,不能复用则卸载旧节点、挂载新节点
→ 需要时更新或新建 RenderObject
→ layout / paint

这就是为什么:

  • build 很常见,但 dispose 不常见
  • 页面返回时你看到的可能只是重新 build
  • 改 Key 可能让本来可复用的 State 直接丢失

观察点:做列表重排、条件渲染、切换 Key 时,看日志里是单纯 build,还是出现 deactivate / dispose。

对应 Lab:flutter-lifecycle-observer · lifecycle_observer_page.dart

2. StatefulWidget 常见生命周期顺序与职责边界 ​

最常见的首次进入顺序:

text
createState
→ initState
→ didChangeDependencies
→ build

交互或依赖变化后,常见会继续出现:

text
setState / inherited dependency changed / parent rebuild
→ build

离开树时常见顺序:

text
deactivate
→ (若重新插回树,可能再次 build)
→ (若最终不再复用) dispose

一个更贴近真实工程的顺序可以记成:

text
首次挂载:initState → didChangeDependencies → build
页面交互:setState → build
父级重建:build
依赖变化:didChangeDependencies → build
路由切换/树调整:deactivate → (可能重新 build,也可能 dispose)
最终销毁:dispose

initState ​

适合放:

  • 一次性 controller 创建:AnimationController、ScrollController、TextEditingController
  • 首次 listener 绑定
  • 首次发起“只想做一次”的初始化逻辑

不适合放:

  • 依赖 context.dependOnInheritedWidgetOfExactType 的逻辑
  • 需要每次回到前台都刷新的逻辑
  • 直接依赖路由返回结果的逻辑

didChangeDependencies ​

适合放:

  • 首次依赖 InheritedWidget / Provider / Localizations / MediaQuery 的初始化
  • 依赖对象变化后需要重算的逻辑

build ​

适合放:

  • 纯 UI 描述
  • 基于当前 state 组装 widget tree
  • 轻量、可重复执行的派生计算

不适合放:

  • 网络请求
  • 打点去重逻辑
  • controller 创建
  • 任何“只能执行一次”的副作用

deactivate ​

deactivate 表示当前 Element 正在离开树,但 不代表一定销毁。常见于:

  • 路由切换时节点暂时移出
  • 带 GlobalKey 的子树移动位置
  • 树重排时旧节点先卸下再尝试复用

所以:

  • 不要把核心释放逻辑写在 deactivate
  • 看到 deactivate 不要立刻下结论“页面死了”

dispose ​

真正适合放资源释放:

  • controller.dispose()
  • streamSubscription.cancel()
  • focusNode.dispose()
  • WidgetsBinding.instance.removeObserver(this)

要点:

  • dispose 后 State.mounted == false
  • 异步回调回来前要先判断 mounted
  • dispose 通常意味着这个 State 不再复用

3. 为什么 build 会频繁执行,但 initState / dispose 不会一一对应 ​

Android 背景工程师最容易误会的是:把 build 想成“View 创建”。实际上 build 更像“重新描述当前 UI 应该长什么样”。

高频触发 build 的原因包括:

  • 当前页面 setState
  • 父组件重建
  • InheritedWidget 依赖变化
  • 屏幕尺寸、主题、文本缩放、语言等环境变化
  • 路由返回后上层页面重新变为活跃并触发重绘

因此:

  • build 多次执行是正常行为,不是性能 bug 本身
  • 真正的问题是你是否把“昂贵副作用”塞进了 build
  • 性能调优时应先分清 rebuild 次数多 与 rebuild 代价高 是两件事

4. AppLifecycleState:处理的是宿主 App 前后台,不是页面 push/pop ​

Flutter 提供:

dart
WidgetsBindingObserver.didChangeAppLifecycleState(AppLifecycleState state)

常见状态可按“观察意义”理解:

状态典型含义你应关注什么
resumedApp 回到前台、可交互恢复轮询、动画、前台埋点、前台资源
inactive前台但临时失焦中间态,系统弹窗/切换过程里常见
hidden对用户已不可见(部分平台更明显)可视性变化,通常作为过渡态参考
pausedApp 进入后台,不再处理前台交互暂停轮询、动画、摄像头、定位等高成本前台工作
detachedEngine 与宿主视图分离进程/引擎层收尾边界,业务里较少主动依赖

不同平台和系统版本可观察到的状态顺序不完全一致;工程上更重要的是理解这些状态表达的“资源与可见性语义”。

一个常见切后台序列可近似理解为:

text
resumed
→ inactive
→ hidden(部分平台)
→ paused

最小可用示例:注册 Observer 并暂停/恢复前台资源 ​

dart
class AppLifecycleWatcher extends StatefulWidget {
  const AppLifecycleWatcher({super.key, required this.child});
  final Widget child;
  @override
  State<AppLifecycleWatcher> createState() => _AppLifecycleWatcherState();
}

class _AppLifecycleWatcherState extends State<AppLifecycleWatcher>
    with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this); // 注册:开始接收前后台事件
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this); // 必须成对移除,否则泄漏
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        _resumePolling();   // 回前台:恢复轮询 / 动画 / 埋点
      case AppLifecycleState.paused:
        _pausePolling();    // 退后台:暂停高成本前台工作
      default:
        break;
    }
  }
}
  • 可能执行顺序:App 切后台 → 系统回调 paused → 暂停轮询;回到前台 → 回调 resumed → 恢复。
  • 观察重点:addObserver 与 removeObserver 必须成对;同时这段逻辑与页面 dispose 无关——退后台时 route 还在、页面没销毁,所以暂停/恢复只能靠 AppLifecycleState 驱动。

回前台则可能反向出现:

text
paused
→ hidden / inactive
→ resumed

什么时候该用 AppLifecycleState ​

  • App 级轮询、心跳、WebSocket 前台降频
  • 页面还在栈里,但用户已经把 App 切后台
  • 摄像头、定位、音视频、动画需要跟随前后台状态切换
  • 前后台埋点与曝光统计

什么时候不该只靠 AppLifecycleState ​

  • 单纯页面 A → 页面 B 的 push/pop
  • 只关心当前 route 是否在栈顶
  • 只关心某个 tab 是否可见

这类情况更应结合:

  • Navigator / route observer
  • 页面自身 state
  • 上层状态管理作用域

5. 页面重建与状态保存:为什么“没销毁”也可能看起来像重新进了一次页面 ​

Flutter 页面重建不等于页面销毁。常见要分 4 类状态:

状态类型典型内容页面 rebuild 后是否保留route pop 后是否保留App 被系统杀掉后是否保留
局部临时 UI 状态bool isExpanded、输入框焦点若同一 State 复用则保留页面被销毁则不保留不保留
控制器/订阅TextEditingController、AnimationController同一 State 复用时保留dispose 后不保留不保留
路由栈内页面状态列表滚动位置、tab 内部页取决于是否仍在栈中 / 是否 keep alive被 pop 后通常不保留不保留
可恢复业务状态草稿、过滤条件、表单进度可通过状态层或恢复机制恢复可恢复需要显式持久化/恢复

影响状态是否保留的关键因素 ​

  1. Element / State 是否被复用
  2. Widget 的位置、类型、Key 是否保持稳定
  3. 页面是否只是被其他 route 覆盖,还是已经被 pop / replace
  4. 状态是存在当前页面 State、更上层状态容器,还是持久化层

常见保存策略 ​

目标常见做法适用场景
页面被盖住再回来仍保留内存状态保持 route 不被 pop;必要时配合 AutomaticKeepAliveClientMixintab 页、分页容器
rebuild 不丢局部 state保持稳定树位置与 Key;避免无意更换 runtimeType条件渲染、列表重排
页面 pop 再进仍想恢复业务状态提升到上层状态管理(如 Riverpod / Provider / BLoC)列表筛选、草稿
宿主回收或冷启动后恢复持久化 + 恢复机制(如本地存储、Restoration)草稿、未提交表单、用户偏好

Android 背景常见误判 ​

  • Android 里常用 ViewModel / SavedStateHandle 兜配置变更与进程恢复
  • Flutter 里如果只把状态塞在当前 State 里,它只能覆盖“当前 Element 还活着”的情况
  • 一旦 route 被 pop、key 变化、宿主进程被杀,就必须靠更上层状态或持久化恢复

观察点:

  • 触发 setState 后,看输入、计数、滚动位置是否保留
  • push 到详情页再 pop,看列表页是否只是 build 还是整页重新创建
  • 人为改 Key,观察原有 State 是否被强制丢弃

6. Flutter 与原生宿主生命周期映射:可以类比,但不能硬等价 ​

Flutter 页面通常运行在一个宿主 Activity / Fragment / FlutterViewController 内,因此存在两条并行生命周期线:

  1. 宿主生命周期:Android Activity / iOS UIApplication / UIViewController
  2. Flutter 树生命周期:Widget / Element / State / RenderObject

一个实用对照表如下:

关注点Flutter 层信号Android 宿主更接近谁iOS 宿主更接近谁关键差异
首次创建页面状态initStateonCreateviewDidLoadFlutter 是页面 state 创建,不等于宿主窗口创建
依赖环境首次就绪 / 变化didChangeDependenciesonCreate 后读取依赖 / config 变化trait / environment 更新Flutter 更强调 inherited 依赖传播
重新描述 UIbuildinvalidate / requestLayout / Compose recompositionsetNeedsLayout / body 重算build 远比宿主创建更频繁
页面暂时离树deactivateView detach / Fragment view 变动view hierarchy 临时调整不代表最终销毁
页面最终释放disposeonDestroy / view 销毁deinit / view controller release只针对当前 Flutter state
App 回前台AppLifecycleState.resumedonStart / onResumeapplicationDidBecomeActive属于宿主 app 前后台,而非单 route
App 退后台AppLifecycleState.pausedonPause / onStopapplicationDidEnterBackground页面可能还在 route 栈中
宿主不可见但未销毁route 仍在栈 + app pausedActivity stoppedapp backgroundedFlutter 页面内存可继续存在

一个典型场景:Flutter 页在 Android 宿主中切后台 ​

常见观察顺序:

text
Flutter 页面已在栈顶
→ 宿主 Activity onPause
→ Flutter 收到 AppLifecycleState.inactive / paused
→ 宿主 Activity onStop

此时:

  • 当前 Flutter route 往往没有 dispose
  • 页面 state 还在内存里
  • 但它已经不该继续做前台资源工作

所以:

  • 页面级资源释放:看 dispose
  • App 前后台暂停/恢复:看 AppLifecycleState
  • Route 栈切换:看 Navigator / route observer

Android / Flutter / Web / Backend 对照 ​

维度FlutterAndroidWebBackend
页面创建initState 创建 StateActivity/Fragment onCreate组件 mount / 首次 render请求进入 handler
页面更新build 反复执行View 刷新 / Compose recompositionrerender无 UI 对应
页面暂离前台route 被覆盖但仍在栈 / App 变 inactiveonPausetab 失焦 / route 被遮挡无直接对应
页面不可见route 可能仍在栈,App 可能 pausedonStop页面 hidden / 缓存无 UI 栈
页面最终销毁disposeonDestroyunmount请求结束 / 对象释放
App 前后台AppLifecycleStateActivity + Process 生命周期Page Visibility / lifecycle进程/worker 级管理
状态恢复上层状态、Restoration、持久化ViewModel / SavedStateHandle / 持久化store + storagecache / DB / session

常见场景 ​

1. 列表页进入详情页再返回 ​

这个场景要解决的不是“页面有没有销毁”这么单一的问题,而是同时观察:

  • 列表页原 State 是否还活着
  • 返回时触发的是重新 build,还是重新 initState
  • 详情页销毁后,父页是否只做轻量恢复,还是把昂贵副作用又跑了一遍

对应最小观察代码可以看:

对应 Lab:flutter-lifecycle-observer · lifecycle_observer_page.dart

这个 lab 里父页有计数器、输入框和 push 到详情页的按钮;详情页返回时会把结果带回父页,便于观察“返回后状态是否保留、哪些日志会再次打印”。

这个场景的概念重点是:返回上一页时,很多时候发生的是“原列表页 State 继续存活,但因为重新成为栈顶而再次 build”,而不是“列表页被销毁后重新创建”。所以下面这组顺序,观察的是 rebuild,不是 recreate。

text
列表页首次:initState → didChangeDependencies → build
打开详情:列表页可能再次 build;详情页 initState → build
返回列表:详情页 dispose;列表页再次 build
  • 预期现象:列表页通常不会立刻销毁;返回时更常见的是再次 build,而不是重新 initState。
  • 观察重点:如果把昂贵初始化写在 build,返回时就会重复执行;判断是否真的重建,要看有没有出现新的 initState / 旧页 dispose。

2. App 退后台时暂停轮询、动画、摄像头 ​

这个场景的概念重点是:App 退后台 和 route 被 push/pop 不是同一类事件。

  • route 变化回答“页面栈怎么变了”
  • AppLifecycleState 回答“宿主 App 现在是否还在前台”

因此像轮询、动画、摄像头、播放器这类资源,不能只等 dispose,而要结合 AppLifecycleState 做暂停与恢复。

这里先把概念钉住:App 退后台时,Flutter 页面常常还留在 route 栈里,没有走到 dispose;真正变化的是宿主 App 的前后台状态。因此下面这些现象对应的是 AppLifecycleState,不是 route 销毁顺序。

对应 Lab:flutter-lifecycle-observer · lifecycle_observer_page.dart

  • 预期现象:当前页面未必 dispose,但会先后收到 inactive / paused 等状态;回前台后再收到 resumed。
  • 观察重点:如果你只在 dispose 停任务,退后台时任务可能继续跑;若回前台需要补刷新,应该在 resumed 做受控恢复,而不是在每次 build 都刷新。

3. 页面重建但状态保留 / 状态丢失 ​

这个场景要先分清两个词:

  • rebuild:重新执行 build,但原 State 可能仍在
  • recreate:原 State 不再复用,需要重新 initState

Flutter 页面里很多“怎么又执行了一遍”的困惑,本质上都是没先分清这两个层级。

常见对照:

  • 同一路由仍在栈里,且 State 未被替换:计数器、controller 往往保留
  • 改了 Key / 换了 widget type:原 State 可能直接丢失
  • route 被 pop 后再重新打开:通常会重新 initState

对应 Lab:flutter-lifecycle-observer · lifecycle_observer_page.dart

  • 预期现象:同一路由仍在栈里且 State 未被替换时,局部状态往往保留;一旦 Key、类型或 route 生命周期变化导致旧 State 不再复用,就会转成真正的 recreate。
  • 观察重点:先区分“rebuild”与“recreate”,再用日志看是否真的走到 dispose;不要看到页面内容刷新了,就直接判定状态被重建。

4. 异步回调晚于页面销毁 ​

这个场景的核心概念是:异步任务的完成时机 和 页面 State 的存活时机 是两条独立时间线。

  • 页面可以先 dispose
  • 异步结果可以稍后才回来
  • 如果回调里仍直接 setState,就会命中经典的 setState() called after dispose()

因此这里真正要建立的是“页面销毁后,回调必须先判断 mounted,且 subscription / timer / stream 要成对清理”的工程习惯。

这里的核心概念是:异步任务和页面生命周期不是一条时间线。任务可能仍在后台继续,页面却已经先被销毁;所以下面的顺序要拿来观察“结果晚到”问题,而不是误以为只要发起任务时页面还活着,回调回来时就一定还能更新 UI。

常见顺序:

text
initState 发起异步任务
→ 用户迅速返回
→ dispose
→ 异步结果回来
→ setState after dispose 报错
  • 预期现象:页面先 dispose 后,晚到结果不应再直接驱动旧 UI;安全实现应在回调前检查 mounted,并让订阅类资源在 dispose 成对清理。
  • 观察重点:异步回调前检查 mounted;subscription / timer / stream 必须在 dispose 取消,否则最容易出现 setState after dispose 与隐性泄漏。

常见坑 ​

坑现象修法
在 build 中做网络请求 / 埋点 / controller 创建返回页面、父组件刷新时重复执行一次性初始化放 initState;持续状态交给状态层
把 App 前后台逻辑写成 route push/pop切后台不生效,前台恢复混乱用 WidgetsBindingObserver 监听 AppLifecycleState
误把 deactivate 当最终销毁过早释放资源,或错误理解日志真正释放放 dispose
忘记在 dispose 解绑 observer / controller / stream内存泄漏、重复回调、setState after dispose在 dispose 成对清理
改动 Key 却没意识到会换掉 State输入框内容、滚动位置、局部状态突然丢失只在确有需要时使用 / 变更 Key
以为 Flutter 页面等同宿主 Activity资源释放时机判断错误分清 route 生命周期、widget 生命周期、app 生命周期

与相近概念对比 ​

概念关注点更适合处理的问题不该替代什么
initState当前 State 首次创建一次性初始化、controller 创建不能替代每次前台恢复
didChangeDependenciesinherited 依赖变化locale / theme / provider 依赖重算不能替代纯 UI 渲染
buildUI 描述重算组装 widget tree不能承载一次性副作用
deactivate暂离元素树观察节点迁移不能替代资源最终释放
dispose当前 State 最终释放解绑、取消订阅、销毁 controller不能替代 App 退后台
AppLifecycleState宿主 App 前后台暂停轮询、动画、摄像头、前后台埋点不能替代 route push/pop
route observer / Navigator页面栈进出页面曝光、返回刷新、路由协作不能替代 App 生命周期
上层状态管理 / 持久化跨页面或跨重建保留状态草稿、过滤条件、会话状态不能替代局部 widget 生命周期

对应实验 ​

各章节内已嵌入跳转链接;此处汇总全部 Lab:

Lab说明源码
flutter-lifecycle-observerflutter-lifecycle-observerlifecycle_observer_page.dart

复习检查题 ​

  1. 为什么说 Flutter 生命周期至少要分成 Widget / Element / RenderObject 三层来理解?

    答:因为 Widget 只是不可变配置,Element 才是挂载实例与复用关系的核心,RenderObject 才负责布局与绘制。只有把三层拆开,才能解释为什么 build 会频繁执行、为什么同一个页面看似“重建了”却没有 dispose。

  2. initState、build、dispose 最核心的职责边界分别是什么?

    答:initState 负责当前 State 的一次性初始化与资源创建;build 负责可重复执行的 UI 描述;dispose 负责最终解绑与释放。把一次性副作用塞进 build,或把 App 前后台逻辑塞进 dispose,都会导致时机错误。

  3. 为什么 App 退后台时不能只依赖页面 dispose?

    答:因为 App 切后台时当前 route 往往还留在栈里,页面 state 仍然存活,不一定触发 dispose。这时应通过 AppLifecycleState 暂停轮询、动画、摄像头等前台资源。

  4. Flutter 页面返回时“重新 build”和“重新创建 State”有什么本质区别?

    答:重新 build 表示当前 Element / State 仍被复用,只是 UI 重新描述;重新创建 State 则意味着旧状态已不再复用,通常会重新走 initState,局部内存状态也可能随之丢失。判断依据是是否真的走到了 dispose,以及 Widget 的位置、类型、Key 是否保持稳定。

  5. Flutter 与 Android 宿主生命周期映射时,最容易误判的点是什么?

    答:最容易误判的是把 Flutter route 或 widget 生命周期直接等同 Android Activity 生命周期。实际上 Flutter 至少并行存在 route 生命周期、widget/state 生命周期、app 生命周期三条线;宿主 onPause/onStop 时 Flutter 页面可能还没 dispose。

速记 ​

  • Flutter 不是单层 View 树,而是 Widget 配置 + Element 挂载 + RenderObject 渲染三层模型
  • initState 做一次性初始化,build 做可重复 UI 描述,dispose 做最终释放
  • deactivate 只是暂离树,不等于销毁
  • App 前后台看 AppLifecycleState,页面进出栈看 Navigator / route observer
  • rebuild ≠ recreate;没看到 dispose,就别轻易下结论说“页面被销毁了”

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