【老孟Flutter】Stateful 组件的生命周期

老孟导读:关于生命周期的文章共有2篇,第一篇是介绍 Flutter 中Stateful 组件的生命周期。
博客地址:http://laomengit.com/blog/20201227/Stateful%E7%BB%84%E4%BB%B6%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.htmlhtml

第二篇是 Flutter 中与平台相关的生命周期,git

博客地址:http://laomengit.com/blog/20201227/%E7%9B%B8%E5%85%B3%E5%B9%B3%E5%8F%B0%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.htmlgithub

博客中还有更多精彩文章,也欢迎加入 Flutter 交流群。面试

此篇文章介绍 StatefulWidget 组件的生命周期, StatefulWidget 组件的生命周期时很是重要的知识点,就像 Android 中 Activity 的生命周期同样,不只在之后的工做中常常用到,面试也会常常被问到。微信

在 Flutter 中一切皆 组件,而组件又分为 StatefulWidget(有状态)StatelessWidget(无状态)组件 ,他们之间的区别是 StatelessWidget 组件发生变化时必须从新建立新的实例,而 StatefulWidget 组件则能够直接改变当前组件的状态而无需从新建立新的实例。网络

注意:使用的 Flutter 版本 和 Dart 版本以下:框架

Flutter 1.22.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 1aafb3a8b9 (6 weeks ago) • 2020-11-13 09:59:28 -0800
Engine • revision 2c956a31c0
Tools • Dart 2.10.4less

不一样的版本 StatefulWidget 组件的生命周期会有差别。ide

下面的 StatefulWidget 和 State 结构图是StatefulWidget 组件生命周期的概览,不一样版本的差别也能够对比此结构图。函数

生命周期流程图:

下面详细介绍 StatefulWidget 组件的生命周期。

生命周期一:createState

下面是一个很是简单的 StatefulWidget 组件:

class StatefulWidgetDemo extends StatefulWidget {
  @override
  _StatefulWidgetDemoState createState() => _StatefulWidgetDemoState();
}

class _StatefulWidgetDemoState extends State<StatefulWidgetDemo> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

当咱们构建一个 StatefulWidget 组件时,首先执行其构造函数(上面的代码没有显示的构造函数,但有默认的无参构造函数),而后执行 createState 函数。但构造函数并非生命周期的一部分。

当 StatefulWidget 组件插入到组件树中时 createState 函数由 Framework 调用,此函数在树中给定的位置为此组件建立 State,若是在组件树的不一样位置都插入了此组件,即建立了多个此组件,以下:

Row(children: [
  MyStatefulWidget(),
  MyStatefulWidget(),
  MyStatefulWidget(),
],)

那么系统会为每个组件建立一个单独的 State,当组件从组件树中移除,而后从新插入到组件树中时, createState 函数将会被调用建立一个新的 State

createState 函数执行完毕后表示当前组件已经在组件树中,此时有一个很是重要的属性 mountedFramework 设置为 true

生命周期二:initState

initState 函数在组件被插入树中时被 Framework 调用(在 createState 以后),此函数只会被调用一次,子类一般会重写此方法,在其中进行初始化操做,好比加载网络数据,重写此方法时必定要调用 super.initState(),以下:

@override
void initState() {
  super.initState();
  //初始化...
}

若是此组件须要订阅通知,好比 ChangeNotifier 或者 Stream,则须要在不一样的生命周期内正确处理订阅和取消订阅通知。

  • initState 中订阅通知。
  • didUpdateWidget 中,若是须要替换旧组件,则在旧对象中取消订阅,并在新对象中订阅通知。
  • 并在 dispose 中取消订阅。

另外在此函数中不能调用 BuildContext.dependOnInheritedWidgetOfExactType,典型的错误写法以下:

@override
void initState() {
  super.initState();
  IconTheme iconTheme = context.dependOnInheritedWidgetOfExactType<IconTheme>();
}

异常信息以下:

解决方案:

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  context.dependOnInheritedWidgetOfExactType<IconTheme>();
}

上面的用法做为初学者使用的比较少,但下面的错误代码大部分应该都写过:

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

异常信息以下:

解决方案:

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

注意:弹出 AlertDialog 在 didChangeDependencies 中调用也会出现异常,但和上面的异常不是同一个。

生命周期三: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 发生变化时调用此函数,当父组件使用相同的 runtimeTypeWidget.key 从新构建一个新的组件时,Framework 将更新此 State 对象的组件属性以引用新的组件,而后使用先前的组件做为参数调用此方法。

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

此方法中一般会用当前组件与前组件进行对比。Framework 调用完此方法后,会将组件设置为 dirty 状态,而后调用 build 方法,所以无需在此方法中调用 setState 方法。

生命周期六:deactivate

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

生命周期七:dispose

当框架从树中永久移除此 State 对象时将会调用此方法,与 deactivate 的区别是,deactivate 还能够从新插入到树中,而 dispose 表示此 State 对象永远不会在 build。调用完 dispose后,mounted 属性被设置为 false,也表明组件生命周期的结束,此时再调用 setState 方法将会抛出异常。

子类重写此方法,释放相关资源,好比动画等。

很是重要的几个概念

下面介绍几个很是重要的概念和方法,这些并非生命周期的一部分,可是生命周期过程当中的产物,与生命周期关系很是紧密。

mounted

mounted 是 State 对象中的一个属性,此属性表示当前组件是否在树中,在建立 State 以后,调用 initState 以前,Framework 会将 StateBuildContext 进行关联,当 Framework 调用 dispose 时,mounted 被设置为 false,表示当前组件已经不在树中。

createState 函数执行完毕后表示当前组件已经在组件树中,属性 mountedFramework 设置为 true,平时写代码时或者看其余开源代码时常常看到以下代码:

if(mounted){
  setState(() {
    ...
  });
}

强烈建议:在调用 setState 时加上 mounted 判断。

为何要加上如此判断?由于若是当前组件未插入到树中或者已经从树中移除时,调用 setState 会抛出异常,加上 mounted 判断,则表示当前组件在树中。

dirty 和 clean

dirty 表示组件当前的状态为 脏状态,下一帧时将会执行 build 函数,调用 setState 方法或者 执行 didUpdateWidget 方法后,组件的状态为 dirty

cleandirty 相对应,clean 表示组件当前的状态为 干净状态clean 状态下组件不会执行 build 函数。

setState

setState 方法是开发者常常调用的方法,此方法调用后,组件的状态变为 dirty,当有数据要更新时,调用此方法。

reassemble

reassemble 用于开发,好比 hot reload ,在 release 版本中不会回调此方法。

交流

老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com

欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

相关文章
相关标签/搜索