注册

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

一、StatefulWidget

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

image.png

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

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

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


二、StatefulElement

上面讲到 StatefulWidget 中 createElement 会创建一个 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 中又会反过来持有 StatefulElement 和 StatefulWidget(当然,State 的源码我们还没有看到);
  • StatefulWidget 只是负责创建 StatefulElement 和 State,但是并不持有它们。

至此我们已经理清了 StatefulWidgetStatefulElement 和 State 三者之间的关系,关于 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 持有 StatefulElement 、StatefulWidget,这里的泛型 T必须是 StatefulWidget 类型,如下图所示:

image.png

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

  BuildContext get context {
return _element!;
}

那么现在我们可以思考一下 State 中的生命周期方法在何时调用以及在哪里调用呢?从上面我们得出的结论:StatefulElement 持有 State 状态,State 中又会反过来持有 StatefulElement 和 StatefulWidgetStatefulWidget 只是负责创建 StatefulElement 和 State,但是并不持有它们。不难猜测出,应该是在 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#_firstBuild 和 Statefulelement#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,也就是说在在新旧 widget 的 key 和 runtimeType 同时相等时 didUpdateWidget() 就会被调用;

    image.png

    image.png

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

    image.png

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

    image.png


总结

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


作者:feelingHy
链接:https://juejin.cn/post/7180626500951998520
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册