一、什么是状态管理?为什么要状态管理?
一般开发一款应用程序,页面间极有可能需要互相进行数据传递,而这里的数据传递也就是指页面间的状态同步(管理)。
页面内部的状态是可以用StatefulWidget维护其状态,当我们需要使用跨组件的状态时,StatefulWidget 将不再是一个好的选择。在多个 Widget 之间进行交流的时候,虽然你可以使用事件处理的方式解决(setState、callback、EventBus、Notification),但是当嵌套足够深的话,很容易就增大代码耦合度。状态管理就是来帮助我们理清这些关系的!
二、事件处理
1.setState
2.Function callback(类似ios Block)
widget:
class StateManageListItem extends StatelessWidget {
final VoidCallback callback;//声明
const StateManageListItem({this.callback});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: callback,//回调
child: Container(
),
);
}
}
调用:
class StateManagementPage extends StatefulWidget {
@override
_StateManagementPageState createState() => _StateManagementPageState();
}
class _StateManagementPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
brightness: Brightness.light,
centerTitle: true,
title: Text(
'状态管理',
style: HSLTextStyles.textWhite16,
),
),
body: new ListView.separated(
itemCount: _items.length,
physics: AlwaysScrollableScrollPhysics(),
separatorBuilder:(BuildContext context, int index) {
return Divider(height: 1, color: HSLColors.selago);
},
itemBuilder: (BuildContext context, int index) {
return StateManageListItem(
callback: (){
if(index == 0){
showToast('callBack传值');
}
},
);
},
),
);
}
}
3.事件总线-EventBus
在APP中,我们经常会需要一个广播机制,用以跨页面事件通知,比如一个需要登录的APP中,页面会关注用户登录或注销事件,来进行一些状态更新。这时候,一个事件总线便会非常有用,事件总线通常实现了订阅者模式,订阅者模式包含发布者和订阅者两种角色,可以通过事件总线来触发事件和监听事件
单例模式实现全局的事件总线
++Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式,这样就可以保证new EventBus()始终返回都是同一个实例,读者应该理解并掌握这种方法。++
//订阅者回调签名
typedef void EventCallback(arg);
class EventBus {
//私有构造函数
EventBus._internal();
//保存单例
static EventBus _singleton = new EventBus._internal();
//工厂构造函数
factory EventBus()=> _singleton;
//保存事件订阅者队列,key:事件名(id),value: 对应事件的订阅者队列
var _emap = new Map
使用:
//页面A中
...
//监听登录事件
bus.on("login", (arg) {
// do something
});
//登录页B中
...
//登录成功后触发登录事件,页面A中订阅者会被调用
bus.emit("login", userInfo);
4.通知-Notification
通知(Notification)是Flutter中一个重要的机制,在widget树中,每一个节点都可以分发通知,通知会沿着当前节点向上传递,所有父节点都可以通过NotificationListener来监听通知。Flutter中将这种由子向父的传递通知的机制称为通知冒泡(Notification Bubbling)。通知冒泡和用户触摸事件冒泡是相似的,但有一点不同:通知冒泡可以中止,但用户触摸事件不行。
1.定义一个通知类,要继承自Notification类;
class MyNotification extends Notification {
MyNotification(this.msg);
final String msg;
}
2.分发通知。
class NotificationRouteState extends State {
String _msg="";
@override
Widget build(BuildContext context) {
//监听通知
return NotificationListener(
onNotification: (notification) {
setState(() {
_msg+=notification.msg+" ";
});
return true;
},
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Builder(
builder: (context) {
return RaisedButton(
//按钮点击时分发通知
onPressed: () => MyNotification("Hi").dispatch(context),
child: Text("Send Notification"),
);
},
),
Text(_msg)
],
),
),
);
}
}
5.InheritedWidget
InheritedWidget提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据
import 'package:flutter/material.dart';
class InheritedWidgetPage extends StatefulWidget {
@override
_InheritedWidgetPageState createState() => _InheritedWidgetPageState();
}
class _InheritedWidgetPageState extends State {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('数据共享InheritedWidget'),
),
body: Center(
child: ShareDataWidget( //使用ShareDataWidget
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: _TestWidget(),//子widget中依赖ShareDataWidget
),
RaisedButton(
child: Text("Increment"),
//每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
onPressed: () => setState(() => ++count),
)
],
),
),
),
);
}
}
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
final int data; //需要在子树中共享的数据,保存点击次数
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
//如果返回true,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return old.data != data;
}
}
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => new __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享数据
return Text(ShareDataWidget
.of(context)
.data
.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("Dependencies change");
}
}
三、状态管理常用插件
1.Provider
我们往往需要管理不同页面之间的数据共享,在页面功能复杂,状态达到几十个上百个的时候,我们会难以清楚的维护我们的数据状态,这时候就需要跨组件管理。
1、创建Model
import 'package:provider/provider.dart';
class Counter with ChangeNotifier {//1
int _count;
Counter(this._count);
void add() {
_count++;
notifyListeners();//2
}
get count => _count;//3
}
2.使用ChangeNotifierProvider
这里用的全局的
main() {
runApp(ChangeNotifierProvider.value(//1
notifier: Counter(1),//2
child: MyApp(),
));
}
3.使用Provider获取Counter的值
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Home"),
actions: [
FlatButton(
child: Text("下一页"),
onPressed: () =>
Navigator.push(context, MaterialPageRoute(builder: (context) {
return SecondPage();
})),
),
],
),
body: Center(
child: Text("${Provider.of(context).count}"),//1
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of(context).add();//2
},
child: Icon(Icons.add),
),
);
}
}
//第二个页面也获取到Count
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(Provider.of(context)),
),
body: Center(
child: Text("${Provider.of(context).count}"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of(context).add();
},
child: Icon(Icons.add),
),
);
}
}
放一个之前自己学习时写的demo,希望可以帮助新入门的老铁们,有好的建议可以提一下,我们一起进步,奥利给!!!
https://github.com/Baffin-HSL/Flutter_UI