🌙 Element lifecycle
enum _ElementLifecycle {
initial, // The element is instantiated, and when it's inserted into the tree.
active, // The element spends most of its life while it's doing flutter things.
inactive, // The element is a briefly detached from the tree.
defunct, // The element has been unmounted. Disposed is called on the element.
}
2
3
4
5
6
7
🌙 Flutter Widget lifecycle概述
构建阶段:在这个阶段,Flutter 框架会调用
createState
或createStatelessWidget
方法来创建对应的State
对象或Widget
对象。如果是 StatefulWidget,会在这个阶段创建对应的 State 对象。初始化阶段:在 State 对象创建后,会调用
initState
方法来进行初始化操作,比如设置初始状态或订阅一些事件。依赖变化阶段:如果 State 对象依赖的数据发生变化(比如调用
setState
方法),Flutter 框架会重新调用build
方法来构建 UI,以反映最新的状态。更新阶段:当 State 对象发生变化时(比如调用
setState
方法),Flutter 框架会重新构建对应的 Widget 树,更新界面。销毁阶段:当 Widget 不再需要时(比如页面被销毁),Flutter 框架会调用
dispose
方法来释放资源,做一些清理工作。
🌙 StatelessWidget 生命周期
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return Container();
}
}
2
3
4
5
6
7
8
9
10
🌙 StatefulWidget 生命周期
createState()
: 在 StatefulWidget 创建时,其构造函数会被调用,用于初始化 Widget。然后会调用createState()
方法,为 Widget 创建状态对象。initState()
: 这是一个在widgetbuild之前调用的方法。在这个方法中,我们可以初始化build
方法所需的变量。didChangeDependencies()
: 这个方法在initState()
方法之后调用。当状态对象的依赖关系发生变化时调用。它可以被多次调用。build()
: 这个方法在屏幕上显示UI。它返回一个小部件。在initState()
方法之后调用。当调用setState
时,此方法将重新构建。didUpdateWidget(Widget oldWidget)
: 状态更新后重新构建 widget 时触发。它在 build() 之后调用。setState()
: 当需要更新状态时调用此方法。不能在build
方法之后立即调用。调用此方法会重新调用build
方法重建UI。deactivate()
: 当widget弹出时,但在当前帧更改完成之前可能重新插入时调用。dispose()
: 在状态对象完全移除或屏幕弹出时调用此方法。reassemble
:在热加载期间重新组装应用程序时调用(应用级)。mounted
: 这是一个布尔值,当buildContext
分配给widget时变为true
。
if(mounted){
// context is safe
MediaQuery.of(context)...;
}
2
3
4
import 'dart:developer';
import 'package:flutter/material.dart';
class LifecycleDemo2 extends StatefulWidget {
const LifecycleDemo2({super.key});
// ignore: no_logic_in_create_state
State<LifecycleDemo2> createState() {
log('create state');
return _ExampleState();
}
}
class _ExampleState extends State<LifecycleDemo2> {
int _counter = 0;
_ExampleState() {
log('constructor, mounted: $mounted');
}
void initState() {
super.initState();
log('initState, mounted: $mounted');
}
void didChangeDependencies() {
super.didChangeDependencies();
log('didChangeDependencies, mounted: $mounted');
}
void setState(VoidCallback fn) {
log('setState');
super.setState(fn);
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
String text = 'hello!';
Widget build(BuildContext context) {
log('build method');
return Column(
children: [
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('You have pushed the button this many times:'),
// counter text
Text(
'$_counter',
style: Theme
.of(context)
.textTheme
.headlineMedium,
),
// navigation button
Padding(
padding: const EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () {
Navigator.pushReplacementNamed(
context, '/Others/Lifecycle3');
},
child: const Text('去lifecycle3页面'),
),
),
],
),
),
FloatingActionButton(
heroTag: 'LifecycleDemo2',
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
// --- addition ---
// ChildDemo(text: text),
const ChildDemo(text: 'constant child'),
// Button to update text value
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
setState(() => text += ' hello!');
},
child: const Text('Update Text (parent)'),
),
// --- addition ---
],
);
}
void didUpdateWidget(covariant LifecycleDemo2 oldWidget) {
super.didUpdateWidget(oldWidget);
log('didUpdateWidget, mounted: $mounted');
}
void deactivate() {
super.deactivate();
log('deactivate, mounted: $mounted');
}
void dispose() {
super.dispose();
log('dispose, mounted: $mounted');
}
void reassemble() {
super.reassemble();
log('reassemble, mounted: $mounted');
}
}
class ChildDemo extends StatefulWidget {
const ChildDemo({
super.key,
required this.text,
});
final String text;
// ignore: no_logic_in_create_state
State<ChildDemo> createState() {
log('create child state');
return _ChildState();
}
}
class _ChildState extends State<ChildDemo> {
void initState() {
super.initState();
log('child initState, mounted: $mounted');
}
void didChangeDependencies() {
super.didChangeDependencies();
log('child didChangeDependencies, mounted: $mounted');
}
void setState(VoidCallback fn) {
log('child setState');
super.setState(fn);
}
Widget build(BuildContext context) {
log('child build method');
return Column(
children: <Widget>[
// text data displayed
Padding(
padding: const EdgeInsets.all(20),
child: Text(
'child: ${widget.text}',
style: const TextStyle(fontSize: 20),
),
),
// Update button
ElevatedButton(
onPressed: () {
setState(() {});
},
child: const Text('Update from child')),
],
);
}
void didUpdateWidget(covariant ChildDemo oldWidget) {
super.didUpdateWidget(oldWidget);
log('child didUpdateWidget, mounted: $mounted');
}
void deactivate() {
super.deactivate();
log('child deactivate, mounted: $mounted');
}
void dispose() {
super.dispose();
log('child dispose, mounted: $mounted');
}
void reassemble() {
super.reassemble();
log('child reassemble, mounted: $mounted');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
- 当首次进入页面时:
不带子组件:
[log] create state
[log] constructor, mounted: false
[log] initState, mounted: true
[log] didChangeDependencies, mounted: true
[log] build method
带子组件:
[log] create state
[log] constructor, mounted: false
[log] initState, mounted: true
[log] didChangeDependencies, mounted: true
[log] build method
[log] create child state
[log] child initState, mounted: true
[log] child didChangeDependencies, mounted: true
[log] child build method
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 当热更新时:
不带子组件:
[log] reassemble, mounted: true
[log] build method
带子组件:
[log] reassemble, mounted: true
[log] child reassemble, mounted: true
[log] build method
[log] child didUpdateWidget, mounted: true
[log] child build method
2
3
4
5
6
7
8
9
10
- 当离开当前页面时:
带子组件
[log] deactivate, mounted: true
[log] dispose, mounted: true
不带子组件
[log] deactivate, mounted: true
[log] child deactivate, mounted: true
[log] child dispose, mounted: true
[log] dispose, mounted: true
2
3
4
5
6
7
8
9
- 当 state 跟新时时:
不带子组件:
[log] setState
[log] build method
带子组件 - 父组件触发 setState
[log] setState
[log] build method
// 如果子组件是constant child 那么不会打印下面两行
[log] child didUpdateWidget, mounted: true
[log] child build method
带子组件 - 子组件触发 setState
[log] child setState
[log] child build method
2
3
4
5
6
7
8
9
10
11
12
13
14
其他部分代码请查看:https://github.com/Keekuun/hello_flutter/tree/main/lib/others (opens new window)
参考文档:
3.Stateless vs Stateful widget lifecycle in Flutter (opens new window)
🌙 HookWidget lifecycle
如果我们不使用 StatefulWidget
来创建状态,而是使用 HookWidget
, 那么 HookWidget
是否也有生命周期呢?
要使用flutter hooks, 首先需要安装
flutter pub add flutter_hooks
。
flutter_hooks
内置的Hook有:
useEffect
useState
useMemoized
useRef
useCallback
useContext
useValueChanged
flutter_hooks
灵感源自于react hooks
(opens new window) ,可以参考。
- 从
HookWidget
源码可以看到,HookWidget
继承自StatelessWidget
, 他是没有StatefulWidget
相关的生命周期的。
/// A [Widget] that can use a [Hook].
///
/// Its usage is very similar to [StatelessWidget].
/// [HookWidget] does not have any life cycle and only implements
/// the [build] method.
///
/// The difference is that it can use a [Hook], which allows a
/// [HookWidget] to store mutable data without implementing a [State].
abstract class HookWidget extends StatelessWidget {
/// Initializes [key] for subclasses.
const HookWidget({Key? key}) : super(key: key);
_StatelessHookElement createElement() => _StatelessHookElement(this);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
但是, HookWidget
可以使用 useEffect
做一些生命周期的事情。
- 从
StatefulHookWidget
源码可以看到,StatefulHookWidget
继承自StatefulWidget
, 他是有StatefulWidget
相关的生命周期的。
/// A [StatefulWidget] that can use a [Hook].
///
/// Its usage is very similar to that of [StatefulWidget], but uses hooks inside [State.build].
///
/// The difference is that it can use a [Hook], which allows a
/// [HookWidget] to store mutable data without implementing a [State].
abstract class StatefulHookWidget extends StatefulWidget {
/// Initializes [key] for subclasses.
const StatefulHookWidget({Key? key}) : super(key: key);
@override
_StatefulHookElement createElement() => _StatefulHookElement(this);
}
2
3
4
5
6
7
8
9
10
11
12
13
- 使用
StatefulWidget
来创建状态:
import 'package:flutter/material.dart';
class CounterDemo extends StatefulWidget {
const CounterDemo({super.key, required this.title});
final String title;
State<StatefulWidget> createState() => _CounterDemoState();
}
class _CounterDemoState extends State<CounterDemo> {
// 直接创建变量 —— 状态
int _counter = 0;
void _incrementCounter() {
// setState 可以用来更新状态
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme
.of(context)
.textTheme
.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
- 使用
HookWidget
状态来创建状态:
import 'package:flutter/material.dart';
// import flutter_hooks
import 'package:flutter_hooks/flutter_hooks.dart';
class HookCounterDemo extends HookWidget {
const HookCounterDemo({super.key, required this.title});
final String title;
Widget build(BuildContext context) {
// 注意: 需要在 build 中使用 `useState`
// useState 用来创建一个状态
final _counter = useState(0);
// 下面使用 `useEffect` 来监听状态的变更
useEffect(() {
// do something when _counter.value changed.
print('1. _counter.value changed.');
return () {
// cancel something
print('2. after print _counter.value changed.');
};
}, [_counter.value]);
void _incrementCounter() {
// 更新状态
_counter.value++;
}
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${_counter.value}',
style: Theme
.of(context)
.textTheme
.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
// 修改状态
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
- 使用
StatefulHookWidget
来创建状态并监听状态:
class StatefulHookCounterDemo extends StatefulHookWidget {
const StatefulHookCounterDemo({super.key, required this.title});
final String title;
State<StatefulWidget> createState() => _StatefulHookCounterDemoState();
}
class _StatefulHookCounterDemoState extends State<StatefulHookCounterDemo> {
// 直接创建变量 —— 状态
int _counter = 0;
void _incrementCounter() {
// setState 可以用来更新状态
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
final _doubleCount = useState(0);
useEffect(() {
// _doubleCount 总是 _counter 的 2 倍
_doubleCount.value = _counter * 2;
return () {};
// 监听 _counter 的变化
}, [_counter]);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter - ${_doubleCount.value}',
style: Theme
.of(context)
.textTheme
.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
以上例子可以看出,可以使用 HookWidget
来替代 StatefulWidget
来管理状态,当然如果我们需要 StatefulWidget
的生命周期功能,
我们可以使用 StatefulHookWidget
来结合两者使用。
Flutter Hooks – an introduction to managing Widget State efficiently (opens new window)