Widget生命周期
生命周期的基本概念
* 什么是生命周期
* 说白了就是回调方法(函数)
* 让使用者知道封装好的Widget当前处于什么状态
* 有什么作用
* 监听Widget的事件
* 初始化数据
* 创建数据
* 发送网络请求
* 内存管理
* 销毁数据、销毁监听者
* 销毁Timer等等
查看无状态小部件
的生命周期
// 忽略当前文件未使用key的报警
// ignore_for_file: use_key_in_widget_constructors, avoid_print
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Row;
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: MyHomePage(title: 'Flutter Demo Page2'),
),
);
}
}
class MyHomePage extends StatelessWidget {
final String? title;
MyHomePage({this.title}) {
print('构造函数被调用了!');
}
Widget build(BuildContext context) {
print('build方法被调用了!');
// 上面title不传的话,默认展示123,这就是flutter中的空安全
return Center(child: Text(title ?? '123'));
}
}
热重载
的时候生命周期执行了一次。
问题:重新运行
发现生命周期执行了两次
实际上只执行了一次,多出来的一次是Android Studio
的问题,我们不用担心。下面通过其他工具进行验证
- 通过
Xcode
运行查看日志进行验证 - 使用终端执行
$ flutter run -d 'iphone 12'
进行验证
查看有状态小部件
的生命周期
// 忽略当前文件未使用key的报警
// ignore_for_file: use_key_in_widget_constructors, avoid_print
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Row;
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: MyHomePage(title: 'Flutter Demo Page2'),
),
);
}
}
class MyHomePage extends StatefulWidget {
final String? title;
MyHomePage({this.title}) {
print('Widget构造函数被调用了!');
}
@override
_MyHomePageState createState() {
// TODO: implement createState
print('createState来了!');
return _MyHomePageState();
}
}
// <MyHomePage>表示是MyHomePage对象的State
class _MyHomePageState extends State<MyHomePage> {
_MyHomePageState() {
print('State的构造方法');
}
@override
void initState() {
print('State的init方法');
super.initState();
}
Widget build(BuildContext context) {
print('State的build方法被调用了!');
// 上面title不传的话,默认展示123,这就是flutter中的空安全
return Center(child: Text(widget.title ?? '123'));
}
@override
void dispose() {
print('State的dispose');
super.dispose();
}
}
State
也是有状态的,它需要更新数据,当State
状态发生变化,又是怎样的呢?下面给State
添加数据
// <MyHomePage>表示是MyHomePage对象的State
class _MyHomePageState extends State<MyHomePage> {
int _count = 0;
_MyHomePageState() {
print('State的构造方法');
}
@override
void initState() {
print('State的init方法');
super.initState();
}
@override
Widget build(BuildContext context) {
print('State的build方法被调用了!');
return Column(
children: [
ElevatedButton(
onPressed: () {
_count++;
setState(() {});
},
child: const Icon(Icons.add)),
Text('$_count')
],
);
}
@override
void dispose() {
print('State的dispose');
super.dispose();
}
}
点击之后只有State的build方法被调用了
,也就是每次调用setState
都会重新调用build
方法。进入setState
查看源码,主要方法是markNeedsBuild()
,把setState(() {});
替换成调用markNeedsBuild()
,点击+号按钮依然能够触发State的build方法被调用了!
。
数据共享InheritedWidget
有状态小部件
还有一个生命周期方法didChangeDependencies
class _MyHomePageState extends State<MyHomePage> {
int _count = 0;
_MyHomePageState() {
print('State的构造方法');
}
@override
void initState() {
print('State的init方法');
super.initState();
}
@override
Widget build(BuildContext context) {
print('State的build方法被调用了!');
return Column(
children: [
ElevatedButton(
onPressed: () {
_count++;
setState(() {});
},
child: const Icon(Icons.add)),
Text('$_count')
],
);
}
@override
void dispose() {
print('State的dispose');
super.dispose();
}
@override
void didChangeDependencies() {
print('didChangeDependencies');
super.didChangeDependencies();
}
}
didChangeDependencies
方法的调用在build
调用之前,主要用于改变依赖关系
;说到依赖关系,我们就不得不提InheritedWidget数据共享小部件
- 新建
inherited_demo.dart
文件
<!-- main.dart文件 -->
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Row;
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
// 使用InheritedDemo小部件
body: const InheritedDemo(),
),
);
}
}
<!-- inherited_demo.dart文件 -->
import 'package:flutter/material.dart';
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 Column(
children: [
Test1(_count),
ElevatedButton(
onPressed: () {
_count++;
setState(() {});
},
child: const Text('我是按钮'))
],
);
}
}
class Test1 extends StatelessWidget {
final int count;
const Test1(this.count);
@override
Widget build(BuildContext context) {
return Test2(count);
throw UnimplementedError();
}
}
class Test2 extends StatelessWidget {
final int count;
const Test2(this.count);
@override
Widget build(BuildContext context) {
return Test3(count);
throw UnimplementedError();
}
}
class Test3 extends StatefulWidget {
final int count;
const Test3(this.count);
@override
_Test3State createState() => _Test3State();
}
class _Test3State extends State<Test3> {
@override
void didChangeDependencies() {
print('didChangeDependencies来了');
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Text(widget.count.toString());
}
}
重新运行工程didChangeDependencies
方法执行了一次,点击按钮didChangeDependencies
方法并没有执行。
- 创建
数据共享类
// 使用共享类,子组件之间就能共享数据
class _InheritedDemoState extends State<InheritedDemo> {
int _count = 0;
@override
Widget build(BuildContext context) {
// MyData共享类
return MyData(
data: _count,
child: Column(
children: [
Test1(_count),
ElevatedButton(
onPressed: () {
_count++;
setState(() {});
},
child: const Text('我是按钮'))
],
)
);
}
}
// 使用了MyData的子组件才会依赖data的Widget
class _Test3State extends State<Test3> {
@override
void didChangeDependencies() {
print('didChangeDependencies来了');
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
print('哥么我来了!');
// 使用了MyData的子组件才会依赖data的Widget
return Text(MyData.of(context)!.data.toString());
}
}
// 数据共享类
class MyData extends InheritedWidget {
final int data; //需要在子组件中共享的数据(保存点击次数)
//构造方法
const MyData({required this.data, required Widget child})
: super(child: child);
//定义一个便捷方法,方便子组件中的Widget去获取共享的数据
static MyData? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyData>();
}
//该回调决定当前data发生变化时,是否通知子组件依赖data的Widget
@override
bool updateShouldNotify(MyData oldWidget) {
//如果返回true,子部件中依赖数据的Widget(build函数中有数据)的didChangeDependencies会调用!
return oldWidget.data != data;
}
}
小结:Widget的生命周期
* StatelessWidget
* 1. 构造方法
* 2. build方法
* StatefulWidget(包含两个对象Widget 、State)
* Widget构造方法
* Widget的CreateState
* State的构造方法
* State的initState方法
* didChangeDependencies方法(改变依赖关系)
* 依赖(共享数据)的InheritedWidget发生变化之后,didChangeDependencies方法才会调用!
* State的build
* 当调用setState方法,会重新调用build进行渲染!
* setState方法内部主要是利用_element(本质就是context对象)调用markNeedsBuild()
* 当Widget销毁的时候,调用State的dispose方法
Widget树与Render树
Widget
的渲染原理
并不是所有的Widget
都会被独立渲染,只有继承了RenderObjectWidget
才会创建RenderObject
对象;在Flutter
渲染的流程中,有三棵重要的树,Flutter
引擎是针对Render
树进行渲染。
Widget树
、Element树
、Render树
Widget Tree
Widget Tree
是整个UI界面的配置,Flutter
开发者通过Widget Tree
告诉Framework
想要绘制的UI界面是什么样的,这棵树是我们主要打交道的对象。Element Tree
Element Tree
是通过Widget Tree
生成的,其主要作用是维护UI元素的树形结构
,并将Widget
和RenderObject
关联到树上。RenderObject Tree
RenderObject Tree
也是通过Widget Tree
生成的,其主要作用是负责界面的绘制和布局
,是属于底层系统
,Flutter
开发者一般不需要直接操作该树。
Element树
查看Element定义,发现它也是一个抽象类:
abstract class Element extends DiagnosticableTree implements BuildContext {
Element(Widget widget)
: assert(widget != null),
_widget = widget;
Element _parent;
@override
Widget get widget => _widget;
Widget _widget;
RenderObject get renderObject { ... }
@mustCallSuper
void mount(Element parent, dynamic newSlot) { ... }
@mustCallSuper
void activate() { ... }
@mustCallSuper
void deactivate() { ... }
@mustCallSuper
void unmount() { ... }
StatefulElement
和StatelessElement
继承自ComponentElement
, ComponentElement
是继承自Element
的抽象类。
abstract class ComponentElement extends Element { ... }
Element
的生命周期:
Framework
调用Widget.createElement
创建一个Element
实例,记为element
;Framework
调用element.mount(parentElement,newSlot)
,mount
方法中首先调用element
所对应Widget
的createRenderObject
方法创建与element
相关联的RenderObject
对象,然后调用element.attachRenderObject
方法将element.renderObject
添加到渲染树中插槽指定的位置(这一步不是必须的,一般发生在Element
树结构发生变化时才需要重新attach
)。插入到渲染树后的element
就处于“active”状态,处于“active”状态后就可以显示在屏幕上了(可以隐藏)。当有父
Widget
的配置数据改变时,同时其State.build
返回的Widget
结构与之前不同,此时就需要重新构建对应的Element
树。为了进行Element
复用,在Element
重新构建前会先尝试是否可以复用旧树上相同位置的element,element节点在更新前都会调用其对应Widget的canUpdate方法,如果返回true,则复用旧Element,旧的Element会使用新Widget配置数据更新,反之则会创建一个新的Element。Widget.canUpdate主要是判断newWidget与oldWidget的runtimeType和key是否同时相等,如果同时相等就返回true,否则就会返回false。根据这个原理,当我们需要强制更新一个Widget时,可以通过指定不同的Key来避免复用。当有祖先
Element
决定要移除element
时(如Widget树结构发生了变化,导致element对应的Widget被移除),这时该祖先Element就会调用deactivateChild 方法来移除它,移除后element.renderObject
也会被从渲染树中移除,然后Framework
会调用element.deactivate
方法,这时element状态变为inactive状态。inactive
态的element
将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element
,inactive态的element在当前动画最后一帧结束前都会保留,如果在动画执行结束后它还未能重新变成active状态,Framework就会调用其unmount方法将其彻底移除,这时element的状态为defunct,它将永远不会再被插入到树中。如果element要重新插入到Element树的其它位置,如
element
或element
的祖先拥有一个GlobalKey
(用于全局复用元素),那么Framework会先将element从现有位置移除,然后再调用其activate方法,并将其renderObject
重新attach
到渲染树。
StatelessWidget的Element
通过源码分析StatelessWidget
的Element
Flutter断点调试源码的方式
StatefulWidget的Element
class StatefulElement extends ComponentElement {
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
...
_state._element = this;
_state._widget = widget;
...
}
State<StatefulWidget> get state => _state;
State<StatefulWidget> _state;
...
@override
Widget build() => state.build(this);
...
}
在创建StatefulElement
实例时,会调用widget.createState()
赋给私有变量_state
,同时把widget
和element
赋给_state
,从而三者产生关联关系,它的build
方法就是调用state.build(this)
,这里的this
就是StatefulElement
对象自己。
小结:Widget树
、Element树
、Render树
* 每一个Widget创建的时候都会创建一个Element对象
* 调用createElement方法,Element加入Element树中,都会调用mount方法
* RenderElement主要是创建RenderObject对象
* 通过mount方法创建RenderObject对象
* StatefulElement继承ComponentElement
* 调用createState方法,创建state
* 将Widget赋值给State对象
* 调用state的build方法,并且将自己(Element)传出去
* StatelessElement继承ComponentElement
* StatelessWidget会创建Element
* 然后Element创建就会调用mount方法
* mount里面会调用Widget的build方法进行渲染,并且将Element自己传出去
* 主要调用build方法,并且将自己(Element)传出去