这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战
Widget 生命周期
生命周期的基本概念
什么是生命周期
我们使用一个对象的时候,有时会需要知道对象的一个状态,什么时候被创建,什么时候被销毁。我们常用的生命周期方法其实本质上就是回调函数,是 Flutter
封装好的,在 Widget
的不同状态设置对应的回调方法给外部使用。
生命周期的作用
- 初始化数据
- 创建变量、常量
- 发送网络请求
- 监听小部件的事件
- 管理内存
- 销毁数据、销毁监听者、定时器等
Widget 常见的生命周期函数
Widget
生命周期函数我们可以分为两种类型来看,无状态与有状态类型。
无状态 Widget (StatelessWidget)
当无状态 Widget
比渲染的时候会依次调用构造函数与 build
函数。
有状态 Widget (StatefulWidget)
class MyHomePage extends StatefulWidget {
final String? title;
MyHomePage({this.title}) {
print('Widget 构造函数被调用了');
}
@override
_MyHomePageState createState() {
print('createState 函数被调用了');
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
_MyHomePageState() {
print('State 构造函数被调用了');
}
@override
void initState() {
super.initState();
print('State 的 init 函数被调用了');
}
@override
Widget build(BuildContext context) {
print('State build 函数被调用了');
return Center(child: Text(widget.title ?? ''),);
}
@override
void dispose() {
super.dispose();
print('State 的 dispose 函数被调用了');
}
}
复制代码
有状态的 Widget
被渲染的时候会依次调用 StatefulWidget
的构造函数、createState
函数、State
的构造函、initState
函数、build
函数。当热重载的时候会调用 StatefulWidget
的构造函数跟 State
的 build
函数。当调用 setState
方法的时候会调用 build
函数。
通过源码可以看到 setState
其实就是 _element
调用了 markNeedsBuild
函数,只是在此之前做了一些错误的判断,这里 _element
就是 context
。
数据共享部件 InheritedWidget
class MyData extends InheritedWidget {
final int data; //需要在子组件中共享的数据
//构造方法
const MyData({required this.data, required Widget child}) : super(child: child);
//定义一个便捷方法,方便子组件中的 widget 获取共享数据
static MyData? ofContext(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
//该回调决定当前 data 发生变化的时候,是否通知子组件(依赖 data 的子组件)
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
print('调用了 updateShouldNotify 函数');
//如果返回 true,子部件中依赖共享数据的 Widget(build 函数中是否使用共享数据) 的 didChangeDependencies 方法会被调用
return (oldWidget as MyData).data != data;
}
}
class InheritedDemo extends StatefulWidget {
const InheritedDemo({Key? key}) : super(key: key);
@override
_InheritedDemoState createState() => _InheritedDemoState();
}
class _InheritedDemoState extends State<InheritedDemo> {
int _count = 0;
@override
Widget build(BuildContext context) {
return MyData(data: _count, child: Column(
children: [
TextDemo(count: _count),
ElevatedButton(onPressed: () {
_count++;
setState(() {});
}, child: const Icon(Icons.add)),
],
));
}
}
class TextDemo extends StatefulWidget {
final int? count;
TextDemo({this.count});
@override
_TextDemoState createState() => _TextDemoState();
}
class _TextDemoState extends State<TextDemo> {
@override
Widget build(BuildContext context) {
print('调用了 build 函数');
return Text((MyData.ofContext(context) as MyData).data.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('调用了 didChangeDependencies 函数');
}
}
复制代码
在我们开发的过程中一定会遇到这种场景,子部件的数据需要依赖父部件的数据,当层级比较多的话层层传递的方式就会比较麻烦,所以 Flutter
提供一个 InheritedWidget
部件,用来解决这种场景。如上案例中,我们定义了一个负责数据共享的类 MyData
继承于 InheritedWidget
,MyData
的构造方法中有两个参数,data
代表需要共享的数据,child
表示依赖于数据共享的 Widget
。并且我们提供了一个 ofContext
方法,供外界获取数据时候使用。使用的话,我们在 _InheritedDemoState
的 build
方法中初始化 MyData
,在需要获取共享数据的子部件中通过 MyData.ofContext(context)
来获取数据,这里需要注意的是,子组件需要通过 InheritedWidget
来获取共享数据的话,其根组件必须是继承于 InheritedWidget
类的 MyData
。当我们执行 _count++
的时候会调用 updateShouldNotify
方法,在这里我们可以通过返回值来判断是否调用子组件的 didChangeDependencies
方法,类似于发送通知,返回值为 true
的时候就会调用,反之就不调用,我们可以根据需求在 didChangeDependencies
方法中做一些事情。
Flutter 渲染原理
abstract class Widget extends DiagnosticableTree {
Element createElement();
}
复制代码
以上只附上了关键代码,通过源码我们可以看到 Widget
类都会实现 createElement
函数。这里我们对于 Widget
的子类 StatelessWidget
跟 StatefulWidget
的渲染流程分开来看。
StatelessWidget 渲染流程
abstract class StatelessWidget extends Widget {
StatelessElement createElement() => StatelessElement(this);
}
复制代码
StatelessWidget
中 createElement
方法会创建一个 StatelessElement
对象并加入到 Elment
树中,并且返回 StatelessElement
对象,创建 StatelessElement
对象的时候 StatelessWidget
自己作为参数传给 StatelessElement
对象。
class StatelessElement extends ComponentElement {
Widget build() => widget.build(this);
}
复制代码
abstract class ComponentElement extends Element {
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_lifecycleState == _ElementLifecycle.active);
_firstBuild();
assert(_child != null);
}
复制代码
abstract class Element extends DiagnosticableTree implements BuildContext {
Element(Widget widget)
: assert(widget != null),
_widget = widget;
复制代码
通过源码追踪可以看到 StatelessElement
继承于 ComponentElement
,ComponentElement
继承于 Element
,在 Element
的构造方法中外部传入的 widget
会被赋值给 _widget
属性,在 ComponentElement
中会调用 mount
方法, mount
方法中的 _firstBuild
最终会执行 ComponentElement
的 build
方法,并且 StatelessElement
的 build
会执行 widget.build(this)
,并把自己传给外面。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {}
}
复制代码
所以我们外部 StatelessWidget
子类中的 context
就是 Element
对象。
StatefulWidget 渲染流程
abstract class StatefulWidget extends Widget {
@override
StatefulElement createElement() => StatefulElement(this);
}
复制代码
StatefulWidget
同样会执行 createElement
,但是返回的对象是 StatefulElement
类型。
class StatefulElement extends ComponentElement {
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
state._element = this;
state._widget = widget;
}
@override
Widget build() => state.build(this);
}
复制代码
但是 StatefulElement
多了一步就是执行 createState
函数,并且赋值给 _state
属性,并且把外部传入的 widget
赋值给 state._widget
属性,把 this
指针赋值给 state._element
。这也是我们能在 state
中能获取到 widget
的原因。这里 build
方法中执行的是 state.build(this)
。