Flutter|StateFulWidget

2024/9/12 移动端DartFlutter

# Stateful组件生命周期

文章学习来自:https://juejin.cn/post/7011023001093144590 (opens new window)

# 生命周期一:createState

当 StatefulWidget 组件插入到组件树中时,createState 函数由 Framework 调用,每一个组件都会有一个单独的State,当createState函数执行完毕后,有一个非常重要的属性 mounted 被 Framework 设置为 true。

class StatefulWidgetDemo extends StatefulWidget {
  
  _StatefulWidgetDemoState createState() => _StatefulWidgetDemoState();
}
1
2
3
4

# 生命周期二:initState

initState 函数在组件被插入树中时被 Framework 调用(在 createState 之后)。

此函数只会被调用一次,子类通常会重写此方法,在其中进行初始化操作,比如加载网络数据,重写此方法时一定要调用 super.initState(),如下:


void initState() {
  super.initState();
  //初始化...
}
1
2
3
4
5

如果此组件需要订阅通知,比如 ChangeNotifier 或者 Stream,则需要在不同的生命周期内正确处理订阅和取消订阅通知。

例如eventbus。

  • 在 initState 中订阅通知。
  • 在 didUpdateWidget 中,如果需要替换旧组件,则在旧对象中取消订阅,并在新对象中订阅通知。
  • 并在 dispose 中取消订阅。 示例一种在initState中的错误写法:

void initState() {
  super.initState();
  showDialog(context: context, builder: (context) {
    return AlertDialog();
  });
}

1
2
3
4
5
6
7
8

异常信息如下:

dependOnInheritedWidgetOfExactType<_LocalizationsScope>() or dependOnInheritedElement() was Called before _StatefulLifecycleState.initState() completed.

解决方案:


void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
    showDialog(context: context, builder: (context) {
      return AlertDialog(title: Text('AlertDialog'),);
    });
  });
}

1
2
3
4
5
6
7
8
9
10

# 生命周期三:didChangeDependencies

didChangeDependencies 方法在 initState 之后由 Framework 立即调用。

另外,当此 State 对象的依赖项更改时被调用,比如其所依赖的 InheritedWidget 发生变化时,Framework 会调用此方法通知组件发生变化。

此方法是生命周期中第一个可以使用 BuildContext.dependOnInheritedWidgetOfExactType 的方法,此方法很少会被重写,因为 Framework 会在依赖发生变化时调用 build,需要重写此方法的场景是:依赖发生变化时需要做一些耗时任务,比如网络请求数据。

didChangeDependencies 方法调用后,组件的状态变为 dirty,立即调用 build 方法。

# 生命周期四:build

此方法是我们最熟悉的,在方法中创建各种组件,绘制到屏幕上。 Framework会在多种情况下调用此方法:

  • 调用 initState 方法后。
  • 调用 didUpdateWidget 方法后。
  • 收到对 setState 的调用后。
  • 此 State 对象的依存关系发生更改后(例如,依赖的 InheritedWidget 发生了更改)。
  • 调用 deactivate 之后,然后将 State 对象重新插入树的另一个位置。此方法可以在每一帧中调用,此方法中应该只包含构建组件的代码,不应该包含其他额外的功能,尤其是耗时任务。

# 生命周期五:didUpdateWidget

当组件的 configuration 发生变化时调用此函数,当父组件使用相同的 runtimeType 和 Widget.key 重新构建一个新的组件时,Framework 将更新此 State 对象的组件属性以引用新的组件,然后使用先前的组件作为参数调用此方法。


void didUpdateWidget(covariant StatefulLifecycle oldWidget) {
  super.didUpdateWidget(oldWidget);
  print('didUpdateWidget');
}

1
2
3
4
5
6

此方法中通常会用当前组件与前组件进行对比。Framework调用完此方法后,会将组件设置为dirty状态,然后调用build 方法,因此无需在此方法中调用setState方法。

# 生命周期六:deactivate

当框架从树中移除此 State 对象时将会调用此方法,在某些情况下,框架将重新插入 State 对象到树的其他位置(例如,如果包含该树的子树 State 对象从树中的一个位置移植到另一个位置),框架将会调用 build 方法来提供 State 对象适应其在树中的新位置。

# 生命周期七:dispose

当框架从树中永久移除此 State 对象时将会调用此方法,与 deactivate 的区别是,deactivate 还可以重新插入到树中,而 dispose 表示此 State 对象永远不会在 build。

# 几个重要概念

  • mounted 是 State 对象中的一个属性,此属性表示当前组件是否在树中,建议在 setState 前对mounted 进行判断
  • dirty 表示组件当前的状态为脏状态,下一帧时将会执行 build 函数,调用 setState 方法或者 执行 didUpdateWidget 方法后,组件的状态为 dirty
  • clean 与 dirty 相对应,clean 表示组件当前的状态为干净状态,clean 状态下组件不会执行 build 函数

# 执行次数

build 执行几次

  • stl app启动时一次 热重启两次
  • stf app启动时一次 热重启两次
Last Updated: 2024/11/22 17:43:46