文章

Flutter生命周期详解及实践建议

Flutter生命周期详解及实践建议

Flutter生命周期详解及实践建议

Flutter生命周期详解及实践建议

好的,Flutter 的生命周期是一个核心概念,理解它对于构建健壮、高效的应用程序至关重要。Flutter 的生命周期可以分为两个层面:

  1. Widget 生命周期:指单个 Widget 从创建到销毁的整个过程,主要由 State 类的方法管理。
  2. App 生命周期:指整个应用程序从启动到退出的状态变化,由 WidgetsBindingObserver 监听。

下面我将为你详细讲解这两个层面。


1. Widget 生命周期 (基于 StatefulWidget)

StatefulWidget 的生命周期围绕 State 类展开。下图清晰地展示了其完整流程:

1758522239179-0eee397e-2a0a-4dc1-b715-838909910aca.png

核心方法详解:

  1. **createState()**
    • 时机:当 StatefulWidget 被插入到 Widget 树中时,Flutter 框架会立即调用其 createState() 方法来创建对应的 State 对象。
    • 用法:你必须重写此方法并返回你的 State 子类实例。通常你不会在此方法中做太多事情。
  2. **initState()**
    • 时机:在 State 对象被创建后,且只会被调用一次。它是生命周期的第一个“有用”的方法。
    • 用法:这是执行一次性初始化操作的理想位置。
      • 初始化依赖于该 State 对象的数据(如 AnimationController)。
      • 订阅(Subscribe)到 Streams 或 ChangeNotifier
      • 发起网络请求(但需要注意,build 方法可能会在数据返回前被调用)。
    • 重要:你必须首先调用 super.initState()
  3. **didChangeDependencies()**
    • 时机
      • initState() 之后立即被调用。
      • 当此 State 对象所依赖的** InheritedWidget** 发生变化时(例如,主题、Locale 等),该方法会被重新调用
    • 用法:如果你需要执行与 InheritedWidget 相关的初始化操作(例如从 Provider 获取数据),可以在这里进行。你也可以在这里发起网络请求,但需要小心处理以免重复请求。通常更推荐在 initState() 中初始化,然后使用 Provider 的监听机制来更新。
  4. **build()**
    • 时机
      • didChangeDependencies() 之后立即调用。
      • 每当你需要重新绘制 UI 时调用,例如:
        • 调用 setState(() {})
        • 父 Widget 重建并传入了新的配置(widget.someValue 改变)。
        • 所依赖的 InheritedWidget 发生变化。
    • 用法:这是一个必须重写的方法。它根据当前的状态(State)和配置(Widget)来描述用户界面。这个方法应该是一个纯函数,没有副作用(例如,不要在这里修改状态或发起网络请求)。
  5. **didUpdateWidget(oldWidget)**
    • 时机:当父 Widget 重建并重新传入一个相同运行时类型Widget 时,Flutter 会对比新旧 Widget 的配置。如果配置发生了变化,它会更新当前的 widget 属性,然后调用此方法。
    • 用法:用于响应 Widget 配置的更改。例如,如果旧的 Widget 有一个 AnimationController,而新的 Widget 传入了不同的动画参数,你可以在这里停止旧的并初始化新的。通常,你不需要重写此方法,因为 State 对象会自动根据新的 widget.* 属性在 build() 方法中重建。但在处理复杂状态(如动画)时非常有用。
    • 对比setState() 是内部状态变化,didUpdateWidget 是外部配置变化。
  6. **setState()**
    • 时机:当你需要改变 State 对象内部的数据(状态)并希望 UI 随之更新时,你需要在修改数据的回调函数中调用它。
    • 用法:它通知框架:“我的状态已经改变了,请重新调用 build() 方法来更新 UI”。你只能在当前 State 对象是 mounted 的时候调用它。
  7. **deactivate()**
    • 时机:当 State 对象从 Widget 树中暂时移除时调用。最常见的情况是它在树中的位置被另一个 Widget 替换了。注意:同一个 Widget 可以在树的不同位置被移除和重新插入。
    • 用法:很少使用。可用于在 Widget 暂时消失前保存一些状态。
  8. **dispose()**
    • 时机:当 State 对象被永久地从 Widget 树中移除时调用。这是生命周期的终点,该方法只会被调用一次
    • 用法这是进行清理工作的关键位置,必须在此处释放所有资源以避免内存泄漏。
      • 取消计时器(Timer)。
      • 取消动画(AnimationController.dispose())。
      • 取消对 Streams 或 ChangeNotifier 的订阅。
    • 重要:你必须调用 super.dispose()

mounted 属性

  • 这是 State 类的一个 bool 属性。当 createState() 被调用后,mounted 被设为 true。当 dispose() 被调用后,它被设为 false
  • 重要规则:在调用 setState() 之前,必须检查 if (mounted) { ... },尤其是在异步回调中(如网络请求、Future.delayed)。因为在回调执行时,Widget 可能已经被销毁(dispose),此时调用 setState() 会抛出异常。

2. App 生命周期

App 生命周期指的是整个应用程序所处的状态(如前台运行、退到后台、挂起等)。你可以通过混入 WidgetsBindingObserver 来监听这些状态变化。

使用方法:

**State 类混入**** ****WidgetsBindingObserver**

  1. dart
1
2
3
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  // ...
}

在**** ****initState** ****中注册监听器**:

  1. dart
1
2
3
4
5
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addObserver(this); // 注册监听
}

重写**** ****didChangeAppLifecycleState** ****方法**:

  1. dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  super.didChangeAppLifecycleState(state);
  print('App state changed to: $state');
  // 根据不同的状态执行操作
  switch (state) {
    case AppLifecycleState.inactive:
      // 应用处于非活动状态,无法接收用户输入(如接电话、分屏)。
      break;
    case AppLifecycleState.paused:
      // 应用对用户不可见,进入后台。
      // 暂停动画、视频、释放不必要的资源。
      break;
    case AppLifecycleState.resumed:
      // 应用重新可见,回到前台。
      // 重新开始动画、恢复逻辑。
      break;
    case AppLifecycleState.detached:
      // 应用仍托管在Flutter引擎上但已从宿主View中分离(很少见)。
      break;
    case AppLifecycleState.hidden:
      // 应用完全隐藏(如切换到其他应用,但未完全进入后台栈,某些系统)。
      break;
  }
}

在**** ****dispose** ****中取消监听**:

  1. dart
1
2
3
4
5
@override
void dispose() {
  WidgetsBinding.instance.removeObserver(this); // 取消监听
  super.dispose();
}

总结与实践建议

方法调用次数常用用途
**initState**1次初始化依赖、订阅、动画控制器、一次性请求。
**didChangeDependencies**≥1次处理与 InheritedWidget 相关的初始化或变化。
**build**≥1次构建UI。必须是纯函数。
**didUpdateWidget**≥0次响应外部配置(Widget)的变化,更新内部状态。
**setState**≥0次通知框架内部状态已变,需要重建UI。
**deactivate**≥0次Widget 被暂时移除时保存临时状态。
**dispose**1次关键:清理资源,防止内存泄漏。
**didChangeAppLifecycleState**≥0次监听App全局状态,管理全局资源(如摄像头、音频)。

黄金法则

  • 初始化在**** ****initState**清理在**** ****dispose**
  • 在异步回调中调用 setState 前,**务必检查**** ****mounted**
  • 保持 build 方法纯净,不要在其中修改状态或执行耗时操作。
本文由作者按照 CC BY 4.0 进行授权