Flutter的生命周期
生命周期的概念:
-
什么是
生命周期:- 生命周期就是回调函数
- 让外界知道封装好的这个
Widget处于什么状态
-
生命周期的作用:
-
初始化数据:
- 创建常量、变量
- 发送网络请求
-
监听小部件的事件
-
管理内存:
- 销毁数据、销毁监听者
- 销毁
Timer等
-
StatelessWidget生命周期探究
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('生命周期Demo')
),
body: MyHomePage(title: 'Stateless生命周期Demo'),
)
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({super.key,required this.title}) {
print('调用了构造方法');
}
final String title;
@override
Widget build(BuildContext context) {
print('调用build方法');
return Center(
child: Text(title),
);
}
}
这时发现调用了两次,但这里其实应该只会调用一次,接下来可以使用Xcode和Flutter命令来验证
使用命令flutter run -d 'iPhone 13 Pro Max'
调用热启动或者热重载最后打印结果都只有一次
StatefulWidget生命周期探究
class MyHomePage extends StatefulWidget {
String title;
MyHomePage({Key? key, required this.title}) : super(key: key){
print('调用widget的构造方法');
}
@override
State<MyHomePage> createState() {
print('调用createState');
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
_MyHomePageState() {
print('调用State构造方法');
}
@override
void initState() {
// TODO: implement initState
print('调用State init');
super.initState();
}
@override
Widget build(BuildContext context) {
print('调用State build');
return Container();
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
print('调用didChangeDependencies');
super.didChangeDependencies();
}
@override
void dispose() {
// TODO: implement dispose
print('调用dispose');
super.dispose();
}
}
这里打印的顺序与我们预见的是相同的,那么接下来要稍加改造,加入一些数据更新
class MyHomePage extends StatefulWidget {
String title;
MyHomePage({Key? key, required this.title}) : super(key: key){
print('调用widget的构造方法');
}
@override
State<MyHomePage> createState() {
print('调用createState');
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
int _count = 0;
_MyHomePageState() {
print('调用State构造方法');
}
@override
void initState() {
// TODO: implement initState
print('调用State init');
super.initState();
}
@override
Widget build(BuildContext context) {
print('调用State build');
return Column(
children: [
ElevatedButton(
onPressed: (){
setState(() {
_count++;
});
},
child: const Icon(Icons.add),
),
Text('$_count'),
],
);
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
print('调用didChangeDependencies');
super.didChangeDependencies();
}
@override
void dispose() {
// TODO: implement dispose
print('调用dispose');
super.dispose();
}
}
这里通过点击按钮来对_count进行自增操作,并设置setState方法来重新调用build方法,那么接下来进入setState方法来看看
在这个方法中可以看到除了断言外,最重要的应当是_element!.markNeedsBuild();这一句调用,其中_element的定义则是StatefulElement? _element;
从State中的get方法可以看出,context的本质就是_elemnet,接下来可以进行验证
@override
Widget build(BuildContext context) {
print('调用State build');
return Column(
children: [
ElevatedButton(
onPressed: (){
// setState(() {
// _count++;
// });
(context as StatefulElement).markNeedsBuild();
},
child: const Icon(Icons.add),
),
Text('$_count'),
],
);
}
这里仅仅需要在State中的build方法中进行改变,将context强转类型后调用markNeedsBuild()方法,结果发现同样实现了setState的作用,但是这里并不建议直接这样调用,因为setState存在很多判断,这样更为安全。
共享数据 InheritedWidget
class InheritedDemo extends StatefulWidget {
const InheritedDemo({Key? key}) : super(key: key);
@override
State<InheritedDemo> createState() => _InheritedDemoState();
}
class _InheritedDemoState extends State<InheritedDemo> {
int count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Test1(count: count),
ElevatedButton(
onPressed: (){
count += 1;
setState(() {
});
},
child: const Text('按钮'))
],
);
}
}
class Test1 extends StatelessWidget {
final int count;
const Test1({Key? key, required this.count}) : super(key: key);
@override
Widget build(BuildContext context) {
print('Test1---$count');
return Test2(count: count);
}
}
class Test2 extends StatelessWidget {
final int count;
const Test2({Key? key,required this.count}) : super(key: key);
@override
Widget build(BuildContext context) {
print('test2---$count');
return Test3(count: count);
}
}
class Test3 extends StatefulWidget {
final int count;
const Test3({Key? key, required this.count}) : super(key: key);
@override
State<Test3> createState() => _Test3State();
}
class _Test3State extends State<Test3> {
@override
Widget build(BuildContext context) {
return Center(
child: Text(widget.count.toString()),
);
}
}
如果有需求如上,需要嵌套去传递数据,并且如上述代码中,每次更新数据需要通过Test1-->Test2-->Test3,这样可能会造成不必要的传递,因为可能只有Test3需要,而其它组件并不需要,这样便引出了共享数据InheritedWidget
那么代码需要更改如下(InheritedWidget代码块为inh)
class ShareData extends InheritedWidget {
final int dataCount;
const ShareData({
Key? key,
required this.dataCount,
required Widget child,
}) : super(key: key, child: child);
//定义一个便捷方法,方便子树中的Widget获取共享数据
static ShareData of(BuildContext context) {
final ShareData? result =
context.dependOnInheritedWidgetOfExactType<ShareData>();
assert(result != null, 'No ShareData found in context');
return result!;
}
// 当数据变化时
@override
bool updateShouldNotify(ShareData oldWidget) {
return oldWidget.dataCount != dataCount;
}
}
class InheritedDemo extends StatefulWidget {
const InheritedDemo({Key? key}) : super(key: key);
@override
State<InheritedDemo> createState() => _InheritedDemoState();
}
class _InheritedDemoState extends State<InheritedDemo> {
int count = 0;
@override
Widget build(BuildContext context) {
return ShareData(
dataCount: count,
child: Column(
children: [
const Test1(),
ElevatedButton(
onPressed: () {
count += 1;
setState(() {});
},
child: const Text('按钮'))
],
));
}
}
class Test1 extends StatelessWidget {
const Test1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Test2();
}
}
class Test2 extends StatelessWidget {
const Test2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Test3();
}
}
class Test3 extends StatefulWidget {
const Test3({Key? key}) : super(key: key);
@override
State<Test3> createState() => _Test3State();
}
class _Test3State extends State<Test3> {
@override
Widget build(BuildContext context) {
return Center(
child: Text(ShareData.of(context).dataCount.toString()),
);
}
@override
void didChangeDependencies() {
print('Test3 -- didChangeDependencies');
super.didChangeDependencies();
}
}
渲染
Widget树和Render树
Flutter中有三棵树,分别为Widget树、Element树、Render树,它们之间的关系可以看做Widget是数据层,Render是渲染层,而Element是一个虚拟层,数据层和渲染层不直接进行交互而是通过虚拟层来完成
Flutter引擎渲染是针对于Render树中的对象进行渲染,而并不是所有的Widget对象都会被渲染,只有继承于RenderObjectWidget的对象才可以创建RenderObject并加入RenderObject树中,从而进行渲染
不同的Widget:
-
不会被渲染的
Container:Container就是不会被渲染的一个Widget,那么可以先来看看其继承关系Container-->StatelessWidget-->Widget,其中并不存在RenderObjectWidget,所以它并不会被渲染
-
会被渲染的
Row:Row是一个会被渲染的Widget,先来看看其继承关系Row-->Flex-->MultiChildRenderObjectWidget-->RenderObjectWidget- 在
Flex实现了createRenderObject方法,返回值是RenderFlex对象,其实就是RenderObject
Element树
刚才讨论了并不是所有的Widget都会被渲染,但是所有的Widget都会创建Element
Row的Element树
这里还是先从Row讲起,其继承关系是:Row-->Flex-->MultiChildRenderObjectWidget-->RenderObjectWidget
RenderObjectWidget是一个抽象类,其中有一个方法是createElement(),并且此方法的具体实现是位于MultiChildRenderObjectWidget中
这里很简单只有一句代码,调用MultiChildRenderObjectElement的构造方法,并且将Widget本身作为参数传入
这里会去调用到父类中即RenderObjectElement
同样接着调用到父类即Element
在这里可以看到将外部传入的Widget对象进行了保存,同样在Element类中可以看到get方法
以上过程可以总结为Widget在创建过程中会去创建Element并且在其中保存了Widget对象,就如同文章刚开始的位置论证过context就是element一样,其中可以直接调用到Widget,Widget的存储过程便是这里
StatelessWidget的Element树
StatelessWidget中实现了createElement方法,与上面对Row的分析中相同,也是调用了一个构造方法,并将其本身当做参数传入
同样最终是在Element中对Widget进行了保存
StatefulWidget的Element树
现在对StatefulWidget的Element树来进行讨论
这里过程同上所以此处不再赘述,只不过会给state同时保存一份Widget
那么现在在此处打一个断点,这里应当是断在了MaterialApp处
此处跨过此方法后可以发现下一步会执行mount方法
可以看到是在调用完createElement后会去调用mount方法,也就是说在创建一个新的Element后就要把它添加到Element树,那么就会调用mount这个方法
/// Add this element to the tree in the given slot of the given parent.
///
/// The framework calls this function when a newly created element is added to
/// the tree for the first time. Use this method to initialize state that
/// depends on having a parent. State that is independent of the parent can
/// more easily be initialized in the constructor.
///
/// This method transitions the element from the "initial" lifecycle state to
/// the "active" lifecycle state.
///
/// Subclasses that override this method are likely to want to also override
/// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild],
/// [RenderObjectElement.moveRenderObjectChild], and
/// [RenderObjectElement.removeRenderObjectChild].
///
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.mount(parent, newSlot)`.
@mustCallSuper
void mount(Element? parent, Object? newSlot) {
assert(_lifecycleState == _ElementLifecycle.initial);
assert(widget != null);
assert(_parent == null);
assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
assert(slot == null);
_parent = parent;
_slot = newSlot;
_lifecycleState = _ElementLifecycle.active;
_depth = _parent != null ? _parent!.depth + 1 : 1;
if (parent != null) {
// Only assign ownership if the parent is non-null. If parent is null
// (the root node), the owner should have already been assigned.
// See RootRenderObjectElement.assignOwner().
_owner = parent.owner;
}
assert(owner != null);
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
_updateInheritance();
attachNotificationTree();
}
不同Widget中Element的mount调用
Row中mount
通过上述分析得知在创建Element后要将它添加到Element树便要调用mount方法,那么现在来看看Row中是如何调用mount方法的
先找到MultiChildRenderObjectWidget中的creatElement方法
进入MultiChildRenderObjectElement中找到mount方法
这里又会调用到父类的mount方法,所以找到RenderObjectElement中
继承于RenderObjectWidget的对象才可以创建RenderObject并加入RenderObject树,便是出自于这里了
StatelessWidget中mount
首先找到createElement方法
StatelessElement中未找到mount方法
进入其父类ComponentElement找到mount方法
进入_firstBuild方法
最终找到performRebuild方法
最终调用到StatelessElement中的build方法
这个方法其实是调用了外部继承于StatelessWidget的类中的build方法,同时外部build方法中的参数context其实就是这里的StatelessElement对象
StatefulWidget中mount
同样先找到createElement方法
这里调用createState方法,并且将Widget保存到state中
找到父类中的mount方法
调用到StatefulElement中的_firstBuild方法
super调用到父类ComponentElement中的_firstBuild方法
调用Element中rebuild方法
调用到StatefulElement中performRebuild方法
super调用父类ComponentElement中performRebuild方法
调用StatefulElement中build方法,并调用state中的build方法
总结:
Widget的声明周期:
-
StatelessWidget:- 构造方法
build方法
-
StatefulWidget:-
Widget构造方法 -
Widget的createState方法 -
State构造方法 -
State的initState方法 -
didChangeDependencies方法 (改变依赖关系):- 依赖(共享数据)的
InheritedWidget发生变化后才会调用
- 依赖(共享数据)的
-
State的build-
当调用
setState方法会重新调用build进行渲染setState内部会使用_element(本质就是context)调用markNeedsBuild方法
-
-
当
Widget销毁的时候调用State的dispose方法
-
Flutter的渲染原理:
-
在
Flutter的渲染流程中有三棵树:Widget🌲、Element🌲、Render🌲,Flutter引擎渲染是针对于Render🌲中的对象进行渲染 -
每个
Widget对象创建出来都会创建一个Element对象:-
调用
createElement方法,Element对象加入Element🌲都会调用mount方法 -
StatelessElement继承ComponentElement:- 主要调用
build方法将自己(Element)也可以说是context传递出去
- 主要调用
-
StatefulElement继承ComponentElement:- 调用
createState方法创建state - 将
Widget赋值给state - 调用
state的build方法并将自己(Element)作为参数传递出去
- 调用
-
RenderElement主要是通过mount方法创建RenderObject对象
-