Flutter-widget生命周期
生命周期
- 本质: 回调方法(函数)
- 内容: 让开发者知道当前widget处于什么样的状态
- 作用
- 监听widget的事件
- 初始化数据(创建数据、发送网络请求...)
- 内存管理(销毁数据、移除监听、销毁timer...)
- 阶段
- 初始化(插入渲染树)
- 状态改变(在渲染树中存在)
- 销毁(从渲染树种移除)
图片来自 blog.csdn.net/u011272795/…
Widget生命周期回调函数调用顺序
StatelessWidget
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Scaffold(
appBar: AppBar(
title: Text('life cycle'),
),
body: MyHomePage(title:'hello'),
),
);
}
}
打印
flutter: +++++++StatelessWidget-构造
flutter: +++++++StatelessWidget-build
PS:也许你在自己测试的时候打印顺序是这样子的,构造方法和build方法被调用了两次,这其实是AS的bug。
flutter: +++++++构造
flutter: +++++++build
flutter: +++++++构造
flutter: +++++++build
你可以使用热重载再运行一次或者终端去运行测试项目,也能得到上面的结果
➜ ~ /Users/ricefun/Desktop/Flutter_case/flutter_app_lifecycle
➜ flutter_app_lifecycle flutter run
Launching lib/main.dart on iPhone SE (2nd generation) in debug mode...
Running Xcode build...
└─Compiling, linking and signing... 5.2s
Xcode build done. 18.0s
flutter: +++++++构造
flutter: +++++++build
Syncing files to device iPhone SE (2nd generation)... 128ms
StatefulWidget
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Scaffold(
appBar: AppBar(
title: Text('life cycle'),
),
body: MyHomePage(title:'hello'),
),
);
}
}
class _MyHomePageState extends State<MyHomePage> {
_MyHomePageState(){
print('-----------StatefulWidget-State-构造方法');
}
@override
void initState() {
// TODO: implement initState
super.initState();
print('-----------StatefulWidget-State-initState()');
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
print('-----------StatefulWidget-State-dispose()');
}
@override
void didUpdateWidget(MyHomePage oldWidget) {
// TODO: implement didUpdateWidget
super.didUpdateWidget(oldWidget);
print('-----------StatefulWidget-State-didUpdateWidget()');
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print('-----------StatefulWidget-State-didChangeDependencies()');
}
//当State对象从渲染树中移出的时候,就会调用!即将销毁!
@override
void deactivate() {
super.deactivate();
print('-----------StatefulWidget-State-deactivate()');
}
@override
Widget build(BuildContext context) {
print('-----------StatefulWidget-State-build()');
return Container();
}
}
打印
flutter: -----------StatefulWidget_构造方法
flutter: -----------StatefulWidget-State-构造方法
flutter: -----------StatefulWidget-State-initState()
flutter: -----------StatefulWidget-State-didChangeDependencies()
flutter: -----------StatefulWidget-State-build()
didChangeDependencies())(改变依赖关系)
这个方法在上面的打印中显示是在State-initState()之后,貌似没什么用。但是在配合InheritedWidget使用,就会变的有意思。让我们看个列子:
原始:
class _InheritedDemoState extends State<InheritedDemo> {
int count = 1;//变量
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
//要将count传给Test2
Test2(count),
RaisedButton(
child: Text('我是按钮'),
//setState!
onPressed: () => setState(() {
count++;
}),
)
],
);
}
}
class Test2 extends StatefulWidget {
//声明变量和构造方法
final count;
Test2(this.count);
@override
_Test2State createState() => _Test2State();
}
class _Test2State extends State<Test2> {
@override
Widget build(BuildContext context) {
//通过widget 拿到count 数据
return Text(widget.count.toString());
}
}
在上面的例子中:在按钮点击后,count数值会变化。在子Widget- Test2 中,声明相同变量final count;通过widget.count去获取数据。当widget树层级非常多的时候,需要每个widget都生命相同变量,那么久会繁琐的多。所以可以使用InheritedWidget
### InheritedWidget:能做到其子Widget能共享InheritedWidget的数据
//数据共享!
class MyData extends InheritedWidget {
MyData({this.data, Widget child}) : super(child: child);
final int data; //需要在子Widget中共享的数据!
//提供一个便捷类方法让子Widget轻松访问的共享数据!
static MyData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyData>();
}
@override
bool updateShouldNotify(MyData oldWidget) {
//如果数据发生变化,其依赖InheritedWidget的子Widget就能收到通知,子Widget就会调用build()
return oldWidget.data != data;
}
}
class InheritedDemo extends StatefulWidget {
@override
_InheritedDemoState createState() => _InheritedDemoState();
}
class _InheritedDemoState extends State<InheritedDemo> {
int count = 1;
@override
Widget build(BuildContext context) {
return MyData(//Column被包在了MyData中,MyData其子Widget 都能使用 data字段
data: count,
child: Column(
children: <Widget>[
Test2(count),
RaisedButton(
child: Text('我是按钮'),
//setState!
onPressed: () => setState(() {
count++;
}),
)
],
),
);
}
}
class Test2 extends StatefulWidget {
final count;
Test2(this.count);
@override
_Test2State createState() => _Test2State();
}
class _Test2State extends State<Test2> {
@override
Widget build(BuildContext context) {
print("++++++++++State-build()");
//通过MyData 提供的方法,可以很方便的拿到count 数据
return Text(MyData.of(context).data.toString());
}
@override
void didChangeDependencies() {
print('++++++++++State-didChangeDependencies');
super.didChangeDependencies();
}
}
点击打印
flutter: ++++++++++State-didChangeDependencies()
flutter: ++++++++++State-build()
flutter: ++++++++++State-didChangeDependencies
flutter: ++++++++++State-build()
flutter: ++++++++++State-didChangeDependencies
flutter: ++++++++++State-build()
flutter: ++++++++++State-didChangeDependencies
flutter: ++++++++++State-build()
可以看到每次点击,count数值发生变化,都会调用didChangeDependencies()。并且使用InheritedWidget,能都共享数据。
所以得
didChangeDependencies()调用:
initState()之后调用- 当依赖的
InheritedWidget发生变化时,也会调用,并且低调用多次
关于共享数据
InheritedWidget,日常开发中Theme.of(context).textTheme;、MediaQuery.of(context).size;都是使用这个技术。pub上有优秀的第三方provider。
didUpdateWidget()
文档是上这么说的:Called whenever the widget configuration changes
实际测试中:
- 在widget树中,父widget rebuild时,如果当前widget配置有变化,也会调用
didUpdateWidget(),并且在调用之后,会调用build()
didUpdateWidget()方法之后会调用build()方法,所以在didUpdateWidget()方法中不要调用setState()
deactivate()
在渲染树中被移除时会先调用deactivate()然后调用dispose()。
eg:组件移除,例如页面销毁的时候会依次执行:deactivate > dispose
dispose()
dispose() 方法在 widget 被废弃时被调用。上面例子没有展示 如果你需要执行一些清理操作(比如:listeners),则重写该方法,并在此之后立即调用 super.dispose()。
总结
StatelessWidget 生命周期函数
| 基本顺序 | 函数 |
|---|---|
| 1 | StatelessWidget-构造函数 |
| 2 | StatelessWidget-build() |
StatefulWidget 生命周期函数
| 基本顺序 | 函数 | 细节 | 调用次数 | 是否支持setState |
|---|---|---|---|---|
| 1 | StatefulWidget-构造方法 | 1 | 否 | |
| 2 | StatefulWidget-State-构造方法 | 1 | 否 | |
| 3 | StatefulWidget-State-initState() | 1 | 否 | |
| 4 | StatefulWidget-State-didChangeDependencies() | 1.initState() 之后调用会调用一次 2.当依赖的InheritedWidget发生变化时,也会调用,并且低调用多次 |
>=1 | 否 |
| 5 | StatefulWidget-State-build() | >=1 | 是 | |
| 6 | StatefulWidget-State-deactivate() | >=1 | 否 | |
| 7 | StatefulWidget-State-dispose() | 先调用deactivate()然后调用dispose() | 1 | 否 |
| StatefulWidget-State-didUpdateWidget() | 在widget树中,父widget rebuild时,如果当前widget配置有变化,也会调用didUpdateWidget(),并且在调用之后,会调用build() | >=1 | 是 |
探究
- AB个页面是独立的页面,如果AB页面通过
bottomNavigationBar放置,它们是生命周期函数调用顺序? 经测试:
flutter: -----------PageA-构造方法
flutter: -----------PageB-构造方法
flutter: -----------PageA-_PageAState-构造方法
flutter: -----------PageA-_PageAState-initState()
flutter: -----------PageA-_PageAState-didChangeDependencies()
flutter: -----------PageA-_PageAState-build()
flutter: -----------PageA-_PageAState-build()
flutter: =========================== A切换到B
flutter: -----------PageA-_PageAState-deactivate()
flutter: -----------PageB-_PageBState-构造方法
flutter: -----------PageB-_PageBState-initState()
flutter: -----------PageB-_PageBState-didChangeDependencies()
flutter: -----------PageB-_PageBState-build()
flutter: -----------PageA-_PageAState-dispose()
- BC个页面是独立的页面,重B push到C,又从C pop 到B,它们是生命周期函数调用顺序? 经测试:
flutter: =========================== B push C
flutter: -----------PageC-构造方法
flutter: -----------PageC-_PageCState-构造方法
flutter: -----------PageC-_PageCState-initState()
flutter: -----------PageC-_PageCState-didChangeDependencies()
flutter: -----------PageC-_PageCState-build()
flutter: =========================== C pop B
flutter: -----------PageC-_PageCState-deactivate()
flutter: -----------PageC-_PageCState-dispose()