状态管理
什么是状态管理?
在Flutter 声明式的代码编写范式下,优雅的处理各个Widget 的状态。
为什么要状态管理?
- 解耦。在面对复杂的项目时,可以更好的保证项目的可读性、可拓展性
- 局部刷新。局部的状态改变时,不需要重建整个页面的Widget,只需要构建局部的,这样能提升性能
页面的Widget 树形结构

数据共享
实际开发中经常会碰到某些需求,需多个Widget共享同一份数据,比如点赞数、评论数、主题等等场景。
示例:
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('$_count',style: TextStyle(fontSize: 20),),
BottomWidget(count: _count,callback: changeCount,)
],
),
),
);
}
changeCount(int num){
setState(() {
_count = num;
});
}
}
class BottomWidget extends StatelessWidget {
final int count;
final Function callback;
BottomWidget({this.count,this.callback});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: (){
callback(count -1);
},
child: Icon(Icons.remove),
),
Text('$count'),
FlatButton(
onPressed: (){
callback(count+1);
},
child: Icon(Icons.add),
)
],
);
}
}
InheritedWidget
是Flutter的一个功能型的Widget,它能有效地将数据在当前Widget中向它的子widget树传递,实现数据共享。需要注意,它是将数据自顶向下共享,意即将InheritedWidget作为根Widget,则它下面的子Widget都可获得该Widget持有的数据。
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
int _count = 0;
@override
Widget build(BuildContext context) {
return CountWidget(
_count,
child: Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('$_count',style: TextStyle(fontSize: 20),),
BottomWidget(callback: changeCount,)
],
),
),
),
);
}
changeCount(int num){
setState(() {
_count = num;
});
}
}
/// 继承自InheritedWidget,实现updateShouldNotify方法
class CountWidget extends InheritedWidget{
final int count;
CountWidget(this.count,{Widget child}):super(child:child);
static CountWidget of(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<CountWidget>();
}
@override
bool updateShouldNotify(CountWidget oldWidget) {
return count != oldWidget.count;
}
}
class BottomWidget extends StatelessWidget {
final Function callback;
BottomWidget({this.callback});
@override
Widget build(BuildContext context) {
/// 获取共享的数据
var count = CountWidget.of(context).count;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: (){
callback(count -1);
},
child: Icon(Icons.remove),
),
Text('$count'),
FlatButton(
onPressed: (){
callback(count+1);
},
child: Icon(Icons.add),
)
],
);
}
}
事件通知
以上数据共享处理了多个Widget读取同一份数据的情况,我们还需要处理共享数据被修改的情况,即共享数据被修改时,依赖该数据的Widget应该收到通知。Flutter中的Notification就是处理这种情况的分发机制。需要注意,Notification是一种自底向上传递通知的机制,意即由子Widget向父Widget传递通知。
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
int _count = 0;
@override
Widget build(BuildContext context) {
return CountWidget(
_count,
child: NotificationListener<MyNotification>(
/// 监听通知
onNotification: (notification) {
if(notification.msg == 'add'){
_count ++;
}else{
_count --;
}
setState(() {});
return true;
},
child: Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('$_count',style: TextStyle(fontSize: 20),),
BottomWidget()
],
),
),
),
),
);
}
}
/// 自定义通知
class MyNotification extends Notification {
MyNotification(this.msg);
final String msg;
}
class CountWidget extends InheritedWidget{
final int count;
CountWidget(this.count,{Widget child}):super(child:child);
static CountWidget of(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<CountWidget>();
}
@override
bool updateShouldNotify(CountWidget oldWidget) {
return count != oldWidget.count;
}
}
class BottomWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
var count = CountWidget.of(context).count;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
/// 点击时分发通知
onPressed: () => MyNotification("sub").dispatch(context),
child: Icon(Icons.remove),
),
Text('$count'),
FlatButton(
onPressed: () => MyNotification("add").dispatch(context),
child: Icon(Icons.add),
)
],
);
}
}
如果两个NotificationListener进行嵌套,子NotificationListener的onNotification回调返回false,则表示不阻止继续向上传递,父NotificationListener会收到通知,如果返回值为true,则会终止传递。
这种通知机制的缺点是明显的,它不能自上向下分发通知,话句话说,父控件产生的通知不能发给子控件。
局部刷新
Flutter 给我们提供了两种局部刷新的方式
- 自行封装
StatefulWidget。即将页面拆分封装成一个个小的StatefulWidget,在需要刷新的区域,调用对应的StatefulWidget的setState方法更新局部UI。 - 使用
ValueListenableBuilder控件。该控件本质上仍然是对StatefulWidget的封装,仅帮助我们简化代码,无需自己封装而已。

数据的观察者
当数据发送变化时,UI需要监听到这种变化,由此Flutter中提供了一种对数据的观察者模式。Flutter是一种响应式的框架,以数据驱动UI,因此,UI是作为观察者存在,数据则是被观察者。
ValueNotifier
这里我们将ValueListenableBuilder和ValueNotifier 结合起来使用,就能实现局部刷新的功能
ValueNotifier<String> _name = ValueNotifier<String>('');
ValueListenableBuilder(
builder: (context, value, child) {
return Text(value);
},
valueListenable: _name,
child: Text('张三'),
);
自定义状态管理
InheritedWidget + ValueListenableBuilder + ValueNotifier
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
ValueNotifier<int> _count = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
return CountWidget(
_count,
child: Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ValueListenableBuilder(
builder: (context, value, child) {
return Text('$value',style: TextStyle(fontSize: 20),);
},
valueListenable: _count,
),
BottomWidget()
],
),
),
),
);
}
}
class CountWidget extends InheritedWidget{
final ValueNotifier<int> count;
CountWidget(this.count,{Widget child}):super(child:child);
static CountWidget of(BuildContext context){
return context.dependOnInheritedWidgetOfExactType<CountWidget>();
}
@override
bool updateShouldNotify(CountWidget oldWidget) {
return count.value != oldWidget.count.value;
}
}
class BottomWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
var _count = CountWidget.of(context).count;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: () => _count.value--,
child: Icon(Icons.remove),
),
/// 不会刷新
Text('${_count.value}'),
FlatButton(
onPressed: () => _count.value++,
child: Icon(Icons.add),
)
],
);
}
}
这里有一个问题,ValueNotifier只能监听一个数据,如果我们有多个数据时,则可以模仿ValueNotifier的实现源码,自定义数据模型类继承自ChangeNotifier
跨组件通信
学习了以上许多概念,这里做一个小结,当我们需要跨多个小控件实现数据通信时,可以使用以下三种较常见的方式
- 使用
GlobalKey跨Widget访问 - 使用
ValueNotifier实现数据通知 - 使用第三方封装的库
event_bus基于流的跨组件通信
import 'package:event_bus/event_bus.dart';
EventBus eventBus = new EventBus();
/// 定义事件
class MyEvent{
String text;
MyEvent(this.text);
}
/// 发送事件
eventBus.fire(new MyEvent('test'));
/// 监听事件
eventBus.on<MyEvent>().listen((MyEvent data) => handle(data.text));
/// 处理事件
void handle(String val) {
setState(() {
data = val;
});
}
状态管理框架
公众号“编程之路从0到1”