目录
- 1、StatefulWidget的背后
- 2、StatefulWidget的生命周期
- 2.1创建阶段
- 2.2更新阶段
- 2.3销毁阶段
- 总结:
1、StatefulWidget的背后
flutter开发过程中,我们经常会用到两个组件StatelessWidget和StatefulWidget。前者为无状组件,后者为有状态组件,无状态组件通常在创建后内部的数据无法改变,而有状态组件可以维持内部的数据状态,适合动态组件使用。
/// 无状态组件的定义 | |
class MyApp extends StatelessWidget {} | |
/// 有状态状态组件的定义 | |
class MyApp extends StatefullWidget { | |
State<StatefulWidget> createState() => _MyApp(); | |
} | |
class _MyApp extends State<MyApp> {} |
定义无状态组件相对简单,只需要继承StatelessWidget即可,而有状态组件需要两个类来实现,首先是继承StatefullWidget,然后重写createState方法,返回State的实现类。
刚始我不明白StatefullWidget为何要通过重写createState的方式来实现,后来通过对StatelessWidget的深入,我渐渐的理解了其中的用意。
首先,StatelessWidget和StatefulWidget都的父类来自Widget,而Widget在定义过程中使用了dart的注解@immutable。
@immutable的作用根据官方的解释:被@immutable注解标明的类或者子类都必须是不可变的。
也就是说,继承了StatelessWidget和StatefullWidget的组件都必须为常量组件,可以使用const修饰,而它的构造函数和成员属性需要是常量,不可变的。
class MyApp extends StatelessWidget { | |
/// 常量属性,不加final会警告 | |
final String? title; | |
const MyApp({Key? key, this.title}) : super(key: key); | |
} | |
/// or | |
class MyApp extends StatefulWidget { | |
/// 常量属性,不加final会警告 | |
final String? title; | |
const MyApp({Key? key, this.title}) : super(key: key); | |
@override | |
State<StatefulWidget> createState() => _MyApp(); | |
} | |
/// 调用时可以加const | |
const MyApp() | |
StatelessWidget自然没什么问题,本身它就是无状态组件,创建出来内部数据不会改变,符合@immutable的定义。而StatefullWidget不同,作为有状态组件,需要维持自身的成员属性可变,不能是一个常量。那如何解决呢?就是通过State来保持状态,因为它并不继承Widget,不受@immutable的影响,内部成员可以定义成变量。
还有个问题,为什么Widget非要使用@immutable来注释?
这一切的出发点都是为了减少性能的损耗,提高Widget构建效率。因为常量定义的类,不会重复构建,可以大大提升运行速度,只要明白这一点,就能解了StatefulWidget这样设计的意义。
2、StatefulWidget的生命周期
StatefulWidget通过State来管理状态,同时也提供了相应的生命周期函数,如initState、didUpdateWidget、dispose等,我们只需要重写这些函数,StatefulWidget在执行过程中会在合适的时机调用。
StatefulWidget的生命周期可以分为创建阶段、更新阶段和销毁阶段,下面我们结合一个示例来看下它的执行过程。
/// StatefulWidget demo | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class ColorInheritedWidget extends InheritedWidget { | |
final Color color; | |
const ColorInheritedWidget({ | |
Key? key, | |
required this.color, | |
required Widget child, | |
}) : super(key: key, child: child); | |
bool updateShouldNotify(covariant ColorInheritedWidget oldWidget) { | |
return oldWidget.color != color; | |
} | |
static ColorInheritedWidget? of(BuildContext context) => | |
context.dependOnInheritedWidgetOfExactType<ColorInheritedWidget>(); | |
} | |
class MyApp extends StatefulWidget { | |
final String? title; | |
const MyApp({Key? key, this.title}) : super(key: key); | |
State<StatefulWidget> createState() { | |
print('createState'); | |
return _MyApp(); | |
} | |
} | |
class _MyApp extends State<MyApp> { | |
int value =; | |
bool isDetach = false; | |
Color color = Colors.red; | |
void initState() { | |
super.initState(); | |
print('initState'); | |
} | |
void didChangeDependencies() { | |
super.didChangeDependencies(); | |
print('didChangeDependencies'); | |
} | |
void didUpdateWidget(covariant MyApp oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
print('didUpdateWidget'); | |
} | |
void deactivate() { | |
super.deactivate(); | |
print('deactivate'); | |
} | |
void dispose() { | |
super.dispose(); | |
print('deactivate'); | |
} | |
Widget build(BuildContext context) { | |
print('build'); | |
return ColorInheritedWidget( | |
color: color, | |
child: MaterialApp( | |
home: Scaffold( | |
appBar: AppBar( | |
title: const Text('StatefulWidget demo'), | |
), | |
body: Column( | |
children: [ | |
if (!isDetach) MyAppChild(value: value), | |
MaterialButton( | |
color: Colors.blue, | |
onPressed: () { | |
value++; | |
setState(() {}); | |
}, | |
child: const Text('更新value'), | |
), | |
MaterialButton( | |
color: Colors.blue, | |
onPressed: () { | |
color = color == Colors.red ? Colors.yellow : Colors.red; | |
setState(() {}); | |
}, | |
child: const Text('更新color'), | |
), | |
MaterialButton( | |
color: Colors.blue, | |
onPressed: () { | |
isDetach = !isDetach; | |
setState(() {}); | |
}, | |
child: Text(isDetach ? '添加' : '移除'), | |
) | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
class MyAppChild extends StatefulWidget { | |
final int value; | |
const MyAppChild({Key? key, required this.value}) : super(key: key); | |
State<StatefulWidget> createState() { | |
print('child-createState'); | |
return _MyAppChild(); | |
} | |
} | |
class _MyAppChild extends State<MyAppChild> { | |
void initState() { | |
super.initState(); | |
print('child-initState'); | |
} | |
void didChangeDependencies() { | |
super.didChangeDependencies(); | |
print('child-didChangeDependencies'); | |
} | |
void didUpdateWidget(covariant MyAppChild oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
print('child-didUpdateWidget'); | |
} | |
void deactivate() { | |
super.deactivate(); | |
print('child-deactivate'); | |
} | |
void dispose() { | |
super.dispose(); | |
print('child-dispose'); | |
} | |
Widget build(BuildContext context) { | |
print('child-build'); | |
return Container( | |
width:, | |
height:, | |
color: ColorInheritedWidget.of(context)?.color, | |
child: Text('${widget.value}'), | |
); | |
} | |
} |
2.1创建阶段
运行demo,首次渲染会打印出:
flutter: createState
flutter: initState
flutter: didChangeDependencies
flutter: build
flutter: child-createState
flutter: child-initState
flutter: child-didChangeDependencies
flutter: child-build
从这里我们可以看出,StatefulWidget组件执行的顺序createState -> initState -> didChangeDependencies -> build。
- createState
Widget只是对组件信息的描述,而Element才是最终对Widget的实现。通过Element可以进行Widget的挂载和RenderObject的创建,最终实现UI的渲染。
flutter首帧渲染的过程中,会不断向下遍历Widget树,通过cteateElememt创建Element实例,每创建一个Widget都会对应创建一个Element。例如创建StatefulWidget时会对应创建StatefulElement。
abstract class StatefulWidget extends Widget { | |
const StatefulWidget({ Key? key }) : super(key: key); | |
StatefulElement createElement() => StatefulElement(this); | |
/// ... | |
} |
StatefulElement的构造函数中,执行了StatefulWidget的createState方法,完成对State的调用。
class StatefulElement extends ComponentElement { | |
StatefulElement(StatefulWidget widget) | |
: _state = widget.createState(), | |
super(widget) { | |
state._element = this; | |
state._widget = widget; | |
} | |
/// ... | |
} |
- initState
createState通过StatefulElement构造函数来创建,而initState在firstBuild方法中定义,firstBuild是首帧渲染的时候,通过StatefulElement实例的mount方法进行调用,firstBuild只会执行一次,也就是说initState只会在Widget首次创建次调用。
通常我们会使用initState进行数据的初始化,也可以进行网络请求操作。
/// StatefulElement | |
void _firstBuild() { | |
try { | |
/// 调用initState | |
final Object? debugCheckForReturnedFuture = state.initState() as dynamic; | |
} finally { | |
/// ... | |
} | |
/// 调用didChangeDependencies | |
state.didChangeDependencies(); | |
/// 调用父级ComponentElement类的_firstBuild,再往上到Element的rebuild方法,最后触发的是StatefulElement中的performRebuild方法。 | |
super._firstBuild(); | |
} | |
void performRebuild() { | |
/// _didChangeDependencies标记Element的依赖节点发生改变,didChangeDependencies会再次调用。 | |
if (_didChangeDependencies) { | |
state.didChangeDependencies(); | |
_didChangeDependencies = false; | |
} | |
/// 调用ComponentElement中的performRebuild,最终触发StatefulElement的build方法 | |
super.performRebuild(); | |
} | |
Widget build() => state.build(this); |
2.2更新阶段
在State类中调用了setState方法,会解发组件update流程,它会对比新旧Element,将修改过的组件标记为脏元素。如果Widget依赖InheritedWidget的数据发现变化,会触发didChangeDependencies函数,接着会调用子组件的update方法,在StatefulElement的update方法中执行了didUpdateWidget,最后才执行rebuild进行build的调用,完成UI的更新。
/// StatefulElement | |
'vm:prefer-inline') | (|
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) { | |
/// ... | |
final Element newChild; | |
if (child != null) { | |
bool hasSameSuperclass = true; | |
if (hasSameSuperclass && child.widget == newWidget) { | |
newChild = child; | |
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { | |
/// 更新逻辑会调用child的update方法 | |
child.update(newWidget); | |
newChild = child; | |
} | |
} | |
return newChild; | |
} | |
/// StatefulElement | |
void update(StatefulWidget newWidget) { | |
super.update(newWidget); | |
try { | |
/// 调用state.didUpdateWidget | |
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic; | |
} | |
/// 重新rebuild深入遍历子组件 | |
rebuild(); | |
} |
在demo中,点击“更新value”按钮,会打印出:
flutter: build
flutter: child-didUpdateWidget
flutter: child-build
从这里可以看出父组件setState时,会先走自身的build再触发子组件的didUpdateWidget和build。
2.3销毁阶段
在demo中,点击“移除”按钮,会打印出:
flutter: build
flutter: child-deactivate
flutter: child-dispose
组件移除节点后会调用deactivate,如果该组件被移除节点,然后未被 插入到其他节点时,则会继续调用 dispose 永久移除,并释放组件资源。
总结:
1、StatefulWidget通过State来管理状态数据,目的是为了保持StatefulWidget可常量创建,减少性能的损耗,提高Widget构建效率。
2、StatefulWidget创建阶段生命周期先执行顺序createState -> initState -> didChangeDependencies -> build。可以在initState进行数据初始化、网络请求操作。
3、StatefulWidget更新阶段:build -> child didUpdateWidget -> child build。
4、StatefulWidget销毁阶段:build -> child deactivate -> child dispose。