-
整体流程
- 介绍状态管理的由来、发展历程、用法、源码
- 最后在不使用三方库的情况下,通过控制器和继承式组件,实现一个Provide
为什么需要状态管理
- 官方给的例子里,基础的事件响应是靠点击事件后更新参数,通过setState重新刷新整个页面,
- 他会重新创建整个widget,调用build方法
- 但是有些地方没必要更新,这个刷新就会造成一定程度的浪费


状态管理需要解决几个问题
-
问题1:子组件如何访问外部的变量?
- 通过传值
- 如果嵌套层级过多,就要一层层往下传

-
问题2:作为一个子组件如何修改外部的变量?
- 通过回传函数callback


-
问题3:A组件如何控制B组件的状态?
- 通过控制器
- 借助控制器,实现Button对TextField内文字的操作

less
// 系统的控制器,用来控制文本
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: _controller,
),
ElevatedButton(
onPressed: () {
_controller.clear();
},
child: const Text("clear"),
),
],
),
),
);
}
控制器
-
为什么用控制器
-
不使用控制器的话,
- 如果组件A需要控制组件B中属性,需要将B组件中的属性做状态提升,也就是将B组件中的属性提升到共有父组件
-
这样做有三个问题
- 第一,这要求我们对B组件要有完整的掌控能力,也就是说这枚组件的每一行代码都是我们自己写的,换句话说,如果这个组件不是我们自己写的,比如TextField,我们就不能让他把text信息暴露给我们,也就是说如果我们想为别人封装一枚可以独立运行的组件,就不能简单把内部状态提升出去
- 第二,就算我们将状态提升出去,刷新的时候还是需要通过父组件的setState,这个会导致外面一起重绘
- 第三,提升出去的属性有时候是基础类型,比如double,我们传递 到子组件的时候直接传递的话其实是值传递,要传递指针的话还需要封装成一个对象
-

-
控制器是什么?手写一个控制器
- 将属性封装成一个ChangeNotifier对象,在set数据的时候调用notifyListeners()
- 将与属性相关的组件用ListenableBuilder包装起来,listenable参数绑定ChangeNotifier对象
- set数据时,notifyListeners会通知到builder去刷新
less
class _MyHomePageState extends State<MyHomePage> {
// 自己写的控制器,研究控制器的原理
DoubleHolder dh = DoubleHolder(0.3);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'对比系统的控制器,自己写的控制器,研究控制器的原理',
),
Foo(dh: dh,),
ElevatedButton(
onPressed: () {
dh.value = 1;
},
child: const Text("设为100"),
),
],
),
),
);
}
}
scala
class DoubleHolder extends ChangeNotifier{
double _value;
DoubleHolder(this._value);
double get value => _value;
set value(double newValue){
_value = newValue;
notifyListeners();
}
}
scala
class Foo extends StatefulWidget {
final DoubleHolder dh;
const Foo({super.key,required this.dh});
@override
State<Foo> createState() => _FooState();
}
class _FooState extends State<Foo> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red.withOpacity(0.5),
child: ListenableBuilder(
listenable: widget.dh,
builder: (context, child) {
return Column(
children: [
FlutterLogo(
size: widget.dh.value * 100 + 50,
),
Slider(
value: widget.dh.value,
onChanged: (value) {
setState(() {
widget.dh.value = value;
});
},
),
],
);
},
),
);
}
}
继承式组件
-
这是上面介绍的几种基础的状态管理方式
-
但是项目中的组件一般都是多层嵌套,如果经过状态提升之后,每次都依赖这种简单的传参的方式,很可能每次创建组件都需要在构造函数里传入大量的参数

使用
- 例子:系统现有的继承式组件
arduino
Widget build(BuildContext context) {
MediaQuery.of(context).size;
return ...;
}
- 尝试自己实现一个InheritedWidget

-
实现前先提一个问题:
- 假设上面的红色方块是一个const的组件,在外面的按钮上调用setState,红色方块会不会刷新?
- 如果这个颜色是引用的InheritedWidget里的属性呢?
dart
// 自定义一个
class MyColor extends InheritedWidget {
final Color color;
const MyColor({required super.child,required this.color});
// 核心方法
@override
bool updateShouldNotify(covariant MyColor oldWidget) {
print('updateShouldNotify;${oldWidget.color} -> $color');
return true;
}
static MyColor? maybeOf(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<MyColor>();
}
static MyColor of(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<MyColor>()!;
}
}
- 顶层加个MyColor(继承)
scala
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Color _color = Colors.red;
@override
Widget build(BuildContext context) {
return MyColor(
color: _color,
child: Scaffold(
body: Center(
child: const Foo()
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_color = Colors.black;
});
},
),
),
);
}
}
- 底层调用
scala
class Foo extends StatelessWidget {
const Foo({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: MyColor.of(context).color,
);
}
}
- 结果

原理
- 本质就是全局维护一个Map,对map进行存入、读取。
- 每个value(InheritedElement)有自己的更新操作,他绑定了关联的widget
- 当 InheritedWidget 更新时,先比对数据,数据改变会通知所有依赖它的 Widget 重建
Map的创建和存入
- 自定义一个InheritedWidget
scala
class MyColor extends InheritedWidget {
final Color color;
const MyColor({required super.child,required this.color});
@override
bool updateShouldNotify(covariant MyColor oldWidget) {
return oldWidget.color != color;
}
}
// 使用,最外层包一个
@override
Widget build(BuildContext context) {
return MyColor(
color: _color,
child: Scaffold(...),
);
}
- 渲染三棵树时构建出共有Map
dart
class InheritedElement extends ProxyElement {
void mount(Element? parent, Object? newSlot) {
// 节点激活时调用
_updateInheritance();
...
}
void activate() {
// 脏节点回收时
_updateInheritance();
}
// 每个Element里面都共有一个map的引用,从父类那里获取
//持久化哈希映射:是一种不可变的数据结构,当修改数据时,不会改变原实例,而是创建一个包含修改的新实例,同时尽可能复用原实例的未变部分,
PersistentHashMap<Type, InheritedElement>? _inheritedElements;
// 存入的方法,在mount和activate时调用
@override
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
// 获取父类的_inheritedElements
final PersistentHashMap<Type, InheritedElement> incomingWidgets =
_parent?._inheritedElements ?? const PersistentHashMap<Type, InheritedElement>.empty();
// 存入当前InheritedElement,key是类型,value是本体
_inheritedElements = incomingWidgets.put(widget.runtimeType, this);
}
}
Map的读取以及widget绑定过程
- 一般使用InheritedWidget
scala
class _FooState extends State<Foo> {
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: MyColor.of(context).color,
);
}
static MyColor of(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<MyColor>()!;
}
- 实际上干的事情
scala
abstract class Element extends DiagnosticableTree implements BuildContext {
PersistentHashMap<Type, InheritedElement>? _inheritedElements;
// 读取以及绑定
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
// 向上找,有没有存入过这个类型的InheritedElement
final InheritedElement? ancestor = _inheritedElements == null ? null : _inheritedElements![T];
if (ancestor != null) {
// 匹配的InheritedElement不为空,就建立依赖关系
// 当 InheritedWidget 数据更新时,框架会通知所有依赖它的 Widget 重建
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
// 将当前 Element->this 加入 InheritedElement->ancestor 的依赖列表
ancestor.updateDependencies(this, aspect);
return ancestor.widget as InheritedWidget;
}
dart
class InheritedElement extends ProxyElement {
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
InheritedWidget 更新时,通知所有依赖它的 Widget 重建
less
Color _color = Colors.red;
@override
Widget build(BuildContext context) {
return MyColor(
color: _color,
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Foo(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
setState(() {
_color = Colors.black;
});
},
),
),
);
}
scss
// 更新
@override
void updated(InheritedWidget oldWidget) {
if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {
super.updated(oldWidget);
}
}
@override
void update(InheritedWidget newWidget) {
final oldWidget = widget;
super.update(newWidget);
// 若需要通知依赖者(updateShouldNotify 返回 true)
if (widget.updateShouldNotify(oldWidget)) {
notifyClients(oldWidget);
}
}
// 通知所有依赖的 Element 重建
@protected
void notifyClients(covariant InheritedWidget oldWidget) {
// 遍历所有依赖的 Element
for (final Element dependent in _dependents.keys) {
// 检查依赖是否有效
if (dependent._active) {
// 标记依赖的 Element 为脏节点,并调度重建
dependent.markNeedsBuild();
}
}
}
Provider
使用:

- 外层包一个ChangeNotifierProvider
- 传一个LogoModel构建方法
- 内部组件可以直接通过 watch绑定 LogoModel数据
scala
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => LogoModel(),
child: const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
),
);
}
}
ini
class LogoModel extends ChangeNotifier{
bool _flipX = false;
bool _flipY = false;
double _size = 200.0;
bool get flipX => _flipX;
set flipX(bool value) {
_flipX = value;
notifyListeners();
}
bool get flipY => _flipY;
set flipY(bool value) {
_flipY = value;
notifyListeners();
}
double get size => _size;
set size(double value) {
_size = value;
notifyListeners();
}
}
scala
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Logo(),
ControlPanel(),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
scala
class Logo extends StatelessWidget {
const Logo({super.key});
@override
Widget build(BuildContext context) {
final model = context.watch<LogoModel>();
return Card(
child: Transform.flip(
flipX: model.flipX,
flipY: model.flipY,
child: FlutterLogo(
size: model.size,
),
),
);
}
}
less
class ControlPanel extends StatelessWidget {
const ControlPanel({super.key});
@override
Widget build(BuildContext context) {
var model = context.watch<LogoModel>();
return Card(
margin: const EdgeInsets.all(32),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('flip X:'),
Switch(
value: model.flipX,
onChanged: (value) {
model.flipX = value;
}),
const Text('flip y:'),
Switch(
value: model.flipY,
onChanged: (value) {
model.flipY = value;
}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('size:'),
Slider(
min: 50,
max: 300,
value: model.size,
onChanged: (value) {
model.size = value;
}),
],
),
],
),
),
);
}
}
如何在不使用三方库的情况下,手写一个Provide
原理:
- 主要就是用到控制器和继承式组件
-
数据和我们自己写的布局都是作为参数传递进入的
-
读的过程通过InheritedWidget向上查找数据
-
修改数据后,通过ListenableBuilder刷新他子类,也就是InheritedWidget
-
InheritedWidget刷新时,为什么不会导致我们的布局刷新?
- 因为他是外层传进去的参数,他其实存在最外层stateful里面,而ListenableBuilder刷新不会导致最外层stateful刷新


代码
scala
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MyChangeNotifierProvider(
create: () => LogoModel(),
child: const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
),
);
}
}
class LogoModel extends ChangeNotifier {
bool _flipX = false;
bool _flipY = false;
double _size = 200.0;
bool get flipX => _flipX;
set flipX(bool value) {
_flipX = value;
notifyListeners();
}
bool get flipY => _flipY;
set flipY(bool value) {
_flipY = value;
notifyListeners();
}
double get size => _size;
set size(double value) {
_size = value;
notifyListeners();
}
}
class MyChangeNotifierProvider<T extends Listenable> extends StatefulWidget {
final Widget child;
final T Function() create;
const MyChangeNotifierProvider(
{super.key, required this.child, required this.create});
@override
State<MyChangeNotifierProvider> createState() =>
_MyChangeNotifierProviderState<T>();
static T of<T>(BuildContext context, {required bool listen}) {
if (listen) {
return context
.dependOnInheritedWidgetOfExactType<_MyInheritedWidget<T>>()!
.model;
} else {
return context
.getInheritedWidgetOfExactType<_MyInheritedWidget<T>>()!
.model;
}
}
}
class _MyChangeNotifierProviderState<T extends Listenable>
extends State<MyChangeNotifierProvider<T>> {
late T model;
@override
void initState() {
super.initState();
model = widget.create();
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: model,
builder: (BuildContext context, Widget? child) {
// child是外面传进来的,所以重建的时候不会rebuild
return _MyInheritedWidget(model: model, child: widget.child);
});
}
}
class _MyInheritedWidget<T> extends InheritedWidget {
final T model;
const _MyInheritedWidget({required super.child, required this.model});
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
print("======updateShouldNotify");
return true;
}
}
// 扩展BuildContext
extension Consumer on BuildContext {
// 绑定
T watch<T>() => MyChangeNotifierProvider.of(this, listen: true);
// 只读一次
T read<T>() => MyChangeNotifierProvider.of(this, listen: false);
}
- 使用
scala
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Logo(),
ControlPanel(),
],
),
),
);
}
}
class Logo extends StatelessWidget {
const Logo({super.key});
@override
Widget build(BuildContext context) {
var model =
Consumer(context).watch<LogoModel>();
return Card(
child: Transform.flip(
flipX: model.flipX,
flipY: model.flipY,
child: FlutterLogo(
size: model.size,
),
),
);
}
}
class ControlPanel extends StatefulWidget {
const ControlPanel({super.key});
@override
State<ControlPanel> createState() => _ControlPanelState();
}
class _ControlPanelState extends State<ControlPanel> {
@override
Widget build(BuildContext context) {
var model =
Consumer(context).watch<LogoModel>();
return Card(
margin: const EdgeInsets.all(32),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('flip X:'),
Switch(
value: model.flipX,
onChanged: (value) {
model.flipX = value;
}),
const Text('flip y:'),
Switch(
value: model.flipY,
onChanged: (value) {
model.flipY = value;
}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('size:'),
Slider(
min: 50,
max: 300,
value: model.size,
onChanged: (value) {
model.size = value;
}),
],
),
],
),
),
);
}
}