Flutter 源码阅读 - StatefulWidget 源码分析 & State 生命周期

1,064 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第 6 天,点击查看活动详情

Flutter 源码阅读 - 三棵树流程分析(一)

Flutter 源码阅读 - 三棵树流程分析(二)

Flutter 源码阅读 - 三棵树流程分析(三)


Flutter 源码阅读 - 三棵树流程分析(三) 这篇文章中,笔者粗略的介绍了 StatefulWidget 的大致执行流程,并没有进行深入的分析,这篇文章将深入分析一下它的源码以及 State 的生命周期。

一、StatefulWidget

StatefulWidget 也是继承自 Widget,重写了 createElement,并且添加了一个新的接口 createState,下面我们看一下它的源码:

image.png

看起来是不是很简单,代码不足十行。

  • createElement 方法返回一个 StatefulElement 类型的 Element
  • createState 抽象方法返回一个 State 类型的实例对象。在给定的位置为 StatefulWidget 创建可变状态(state)。框架可以在 StatefulWidget生命周期内多次调用此方法,比如:将 StatefulWidget 插入到 Widget Tree 中的多个位置时,会创建多个单独的 State 实例,如果将 StatefulWidgetWidget Tree 中删除,稍后再次将琦插入到 Widget Tree 中,框架将会再次调用 createState创建一个新的 State 实例对象。

StatefulWidget 我们暂时就先讲到这里, 关于 StateStatefulElement 我们在下面会进行分析。


二、StatefulElement

上面讲到 StatefulWidgetcreateElement 会创建一个 StatefulElement 类型的 Element。下面我们就一起看下 StatefulElement 的源码。

image.png

在执行 StatefulWidget#createElement 时会把 this 传递进去,此时执行 StatefulElement 的构造方法中我们可以看出会做以下三件事情:

  • 首先通过 _state = widget.createState() 执行 StatefulWidget 中的 createState 进行闯将 State 实例;
  • 其次通过 state._element = this 将当前对象赋值给 State 中的 _element 属性;
  • 最后通过 state._widget = widget,将 StatefulWidget 赋值给 State 中的 _widget 属性。

通过以上分析我们相应的可以得出以下结论:

  • StatefulElement 持有 State 状态;
  • State 中又会反过来持有 StatefulElementStatefulWidget(当然,State 的源码我们还没有看到);
  • StatefulWidget 只是负责创建 StatefulElementState,但是并不持有它们。

至此我们已经理清了 StatefulWidgetStatefulElementState 三者之间的关系,关于 State 我们会在后面讲到。现在我们已经知道 StatefulWidget 中的 createState 在何时执行,那么 StatefulElement#createElement 又是在何时执行的呢?下面我们来看一个例子:

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MyApp(),
  );
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return const ColoredBox(
      color: Colors.red,
    );
  }
}

image.png

通过断点调试可以看出在 Element#inflateWidget 中 通过 newWidget.createElement() 来进行触发 StatefulWidget#createElement 的执行,进而执行 StatefulElement 的构造函数。

关于更多 StatefulElement 内部方法,将在 State 源码以及相关案例中穿插进行。


三、State

State 是一个抽象类,它只定义了一个 build 抽象方法,由于构建 Widget 对象。它是通过StatefulElement#build 方法进行调用的。

image.png

如下是 State 源码的部分截图:

image.png

从源码中我们也可以对上面的结论得到验证,State 持有 StatefulElementStatefulWidget,这里的泛型 T 必须是 StatefulWidget 类型,如下图所示:

image.png

除此之外 State 中还持有 BuildContext,通过源码我们可以看出 BuildContext 其实就是 StatefulElement

  BuildContext get context {
    return _element!;
  }

那么现在我们可以思考一下 State 中的生命周期方法在何时调用以及在哪里调用呢?从上面我们得出的结论:StatefulElement 持有 State 状态,State 中又会反过来持有 StatefulElementStatefulWidgetStatefulWidget 只是负责创建 StatefulElementState,但是并不持有它们。不难猜测出,应该是在 StatefulElement 中来触发的,下面我通过一个小的案例来进行研究一下:

void main() {
  runApp(
    const WrapWidget(),
  );
}
class WrapWidget extends StatelessWidget {
  const WrapWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text("StatefulWidget Demo"),
        ),
        body: MyApp(),
      ),
    );
  }
}
class MyApp extends StatefulWidget {
  const MyApp({
    super.key,
  });

  @override
  // ignore: no_logic_in_create_state
  State<MyApp> createState() {
    debugPrint("createState");
    return _MyAppState();
  }
}

class _MyAppState extends State<MyApp> {
  late int _count = 0;

  @override
  void initState() {
    debugPrint("initState");
    super.initState();
  }

  @override
  void didChangeDependencies() {
    debugPrint("didChangeDependencies");
    super.didChangeDependencies();
  }

  @override
  void didUpdateWidget(MyApp oldWidget) {
    debugPrint("didUpdateWidget");
    super.didUpdateWidget(oldWidget);
  }

  @override
  void deactivate() {
    debugPrint("deactivate ");
    super.deactivate();
  }

  @override
  void dispose() {
    debugPrint("dispose");
    super.dispose();
  }

  @override
  void reassemble() {
    debugPrint("reassemble");
    super.reassemble();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint("build");
    return Column(
      children: [
        Text('$_count'),
        OutlinedButton(
          onPressed: () {
            setState(() {
              _count++;
            });
          },
          child: const Text('OnPress'),
        ),
      ],
    );
  }
}

程序刚运行时打印日志如下:

image.png

然后我们点击⚡️按钮热重载,控制台输出日志如下:

image.png

我们再次点击 OnPress 按钮时,打印日志如下:

image.png

此时我们注释掉 WrapWidget 中的 body: MyApp() 这行代码,打印日志如下:

image.png

此时结合源码,我们来一起看下各个生命周期函数:

  • initState: 当 Widget 第一次插入到 Widget Tree 中,会执行一次,我们一般在这里可以做一些初始化状态的操作以及订阅通知事件等,通过源码我们可以看出它是在 Statefulelement#_firstBuild 中执行的;

    image.png

  • didChangeDependencies: 当 State 对象的依赖发生变化时会进行调用,例如:例如系统语言 Locale 或者应用主题等,通过源码我们可以看出它在 Statefulelement#_firstBuildStatefulelement#performRebuild 中都会执行;

    image.png

  • build:在以下场景中都会调用:

    • initState 调用之后
    • didUpdateWidget 调用之后
    • setState 调用之后
    • didChangeDependencies 调用之后
    • 调用 deactivate 之后,然后又重新插入到 Widtget Tree

    通过源码可以看出它是在 Statefulelement#build 中执行的;

    image.png

  • reassemble:专门为了开发调试而提供的,在 hot reload 时会被调用,在 Release 模式下永远不会被调用,通过源码可以看出它是在 Statefulelement#reassemble 中执行的;

    image.png

  • didUpdateWidget:在 Widget 重新构建时,Flutter 框架会在 Element#updateChild 中通过Widget.canUpdate 判断是否需要进行更新,如果为 true 则进行更新;

    image.png

    canUpdate 源码中,新旧 widget 的 key 和 runtimeType 同时相等时会返回 true,也就是说在在新旧 widgetkeyruntimeType 同时相等时 didUpdateWidget() 就会被调用;

    image.png

    image.png

  • deactivate:当 State 对象从树中被移除时将会调用,它将会在 Statefulelement#deactivate 中进行调用;

    image.png

  • dispose:当 State 对象从树中被永久移除时调用;通常在此回调中释放资源,它将会在 Statefulelement#unmount 中进行调用。

    image.png


总结

至此,结合一些小的案例和源码阅读,我们大致明白了 StatefulWidgetState 以及 StatefulElement 他们三者之间的关系以及 State 的生命周期,相信在以后的实际应用中会更加得心应手。