flutter-状态管理5-Redux
2021-04-19 本文已影响0人
浮华_du
这个相对来说比较复杂
1. 先看例子
- 需要引入的库
redux: ^5.0.0
#添加支持异步操作的支持库
redux_thunk: ^0.4.0
1.建立State
- 建立一个共享数据,并提供构造方法
class CountState {
int _count;
get count => _count;
CountState(this._count);
CountState.initState() : _count = 0;
}
2.创建Action
- 建立Actions操作数据,这里是定义两个事件: 加 减
enum Actions { increment, decrement }
class Action {
final Actions type;
Action(this.type);
}
class IncreTestAction extends Action {
int value;
IncreTestAction(this.value) : super(Actions.increment);
}
class DecreTestAction extends Action {
int value;
DecreTestAction(this.value) : super(Actions.decrement);
}
3.创建Action对应的reducer
- 创建action动作对应的reducer,返回State
- 将action合并,以便启动入口处使用
- 另外,我们这里还定义了一个异步的加法
///创建reducer
CountState increReducer(CountState preState, dynamic action) {
switch (action.type) {
case Actions.increment:
return CountState(preState.count + action.value);
default:
return preState;
}
}
CountState decreReducer(CountState preState, dynamic action) {
switch (action.type) {
case Actions.decrement:
return CountState(preState.count - action.value);
default:
return preState;
}
}
/// 合并reducer
final reducers = combineReducers([increReducer, decreReducer]);
/// 异步加
ThunkAction asyncIncrement(int value) {
return (Store store) async {
await Future.delayed(Duration(seconds: 3));
store.dispatch(IncreTestAction(value));
};
}
4.创建中间件
- 创建一个产生中间件的工厂类
- 通过new TypedMiddleware的方式创建中间件
- 创建一个异步处理的中间件俩处理我们的Thunk类型的函数
- 把所有的中间件集合到一起,以便启动入口处使用
///第一步 创建一个产生中间件的工厂类,
///利用generate产生中间件
abstract class MiddlewareFactory {
MiddlewareFactory();
List<Middleware<CountState>> generate();
}
class LoggerMiddle extends MiddlewareFactory {
@override
List<Middleware<CountState>> generate() {
// TODO: implement generate
///第二步 通过new TypedMiddleware的方式创建中间件
return [
TypedMiddleware<CountState, IncreTestAction>(_doIncreLogger),
TypedMiddleware<CountState, DecreTestAction>(_doDecreLogger),
];
}
void _doIncreLogger(
Store<CountState> store, IncreTestAction action, NextDispatcher next) {
next(action);
debugPrint(
"store:${store.state.count}, action type ${action.type}, value ${action.value}");
}
void _doDecreLogger(
Store<CountState> store, DecreTestAction action, NextDispatcher next) {
next(action);
debugPrint(
"store:${store.state.count}, action type ${action.type}, value ${action.value}");
}
}
class ThunkMiddle extends MiddlewareFactory {
@override
List<Middleware<CountState>> generate() {
// TODO: implement generate
return [
/// 创建一个异步处理的中间件俩处理我们的Thunk类型的函数
TypedMiddleware<CountState, ThunkAction>(_doThunk),
];
}
void _doThunk(
Store<CountState> store, ThunkAction action, NextDispatcher next) {
if (action is ThunkAction<CountState>) {
action(store);
} else {
next(action);
}
}
}
///第三步 把所有的中间件集合到一起
List<Middleware<CountState>> initMiddleware() {
List<Middleware<CountState>> middlewares = [];
List<MiddlewareFactory> factories = [
LoggerMiddle(),
///第三步,一起放入到中间件集合
ThunkMiddle(),
];
factories.forEach((factory) => middlewares.addAll(factory.generate()));
return middlewares;
}
5.runApp时初始化Store
- 使用前面创建的reducer 和 中间件,并使用StoreProvider包裹
void main() {
Store store = Store<CountState>(reducers,
initialState: CountState.initState(), middleware:initMiddleware());
runApp(StoreProvider<CountState>(
store: store,
child: MyApp() )
);
}
6.页面内测试使用
1.用于展示 , 操作数据的widget
- 使用StoreConnector获取store,进而可以拿到里面的state,拿到共享数据
/// 展示数值
class StoreConnectorTextWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
///获取store的state
print("StoreConnector展示__build");
return StoreConnector<CountState, int>(
converter: (store) => store.state.count,
builder: (context, vm) {
print("StoreConnector展示__刷新");
return Text(
"Count: $vm",
style: TextStyle(fontSize: 30),
);
},
);
}
// StoreConnector<S, ViewModel>
// 首先这里需要强制声明类型,S代表我们需要从store中获取什么类型的state,ViewModel指的是我们使用这个State时的实际类型。
// 然后我们需要声明一个converter<S,ViewModel>,它的作用是将Store转化成实际ViewModel将要使用的信息,比如我们这里实际上要使用的是count,所以这里将count提取出来。
// builder是我们实际根据state创建Widget的地方,它接收一个上下文context,以及刚才我们转化出来的ViewModel,所以我们就只需要把拿到的count放进Text Widget中进行渲染就好了
}
- 使用StoreConnector或者StoreBuilder,获取store,可以通过store.dispatch发送Action事件,修改数据
//加
class StoreConnectorAddButtonWidget extends StatelessWidget {
final String text;
final int value;
StoreConnectorAddButtonWidget(this.text, this.value) : super();
@override
Widget build(BuildContext context) {
print("StoreConnector_+操作__build");
return StoreConnector<CountState, VoidCallback>(
converter: (Store<CountState> store) {
return () => store.dispatch(IncreTestAction(value)); //发送加value的action
},
builder: (context, callback) {
print("StoreConnector__+操作_刷新");
return TextButton(
child: Text(text),
onPressed: callback,
);
},
);
}
// 我们还是使用StoreConnector<S,ViewModel>。
// 这里由于是发出了一个动作,所以是VoidCallback。
// store.dispatch发起一个action,任何中间件都会拦截该操作,在运行中间件后,操作将被发送到给定的reducer生成新的状态,并更新状态树。
}
//减
class StoreConnectorDecreButtonWidget extends StatelessWidget {
final String text;
final int value;
StoreConnectorDecreButtonWidget(this.text, this.value) : super();
@override
Widget build(BuildContext context) {
return StoreBuilder<CountState>(builder: (context, store) {
return TextButton(
child: Text(text),
onPressed: () {
store.dispatch(DecreTestAction(value));
});
});
// return StoreConnector<CountState, VoidCallback>(
// converter: (Store<CountState> store) {
// return () => store.dispatch(DecreTestAction(value)); //发送减value的action
// },
// builder: (context, callback) {
// return TextButton(
// child: Text(text),
// onPressed: callback,
// );
// },
// );
}
}
/// 异步加
class AsyncAddButton extends StatelessWidget {
final String text;
final int value;
AsyncAddButton(this.text, this.value) : super();
@override
Widget build(BuildContext context) {
print("StoreConnector_异步操作__build");
return StoreConnector<CountState, VoidCallback>(
converter: (Store<CountState> store) {
return () => store.dispatch(asyncIncrement(value)); //发送异步加value的action
},
builder: (context, callback) {
print("StoreConnector_异步操作__刷新");
return TextButton(
child: Text(text),
onPressed: callback,
);
},
);
}
}
2.测试页面
- 展示数据,修改数据,异步修改数据
class ReduxFirstPage extends StatelessWidget {
final String title;
ReduxFirstPage(this.title) : super();
@override
Widget build(BuildContext context) {
print("Redux__first_build");
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: ListView(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: StoreConnectorTextWidget(),
),
TextButton(
child: Text("跳转到第二页"),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return ReduxtSecondPage("第二页");
}));
debugPrint("跳转到第二页");
},
),
AsyncAddButton("异步加二 3s后", 2),
],
));
}
}
class ReduxtSecondPage extends StatelessWidget {
final String title;
ReduxtSecondPage(this.title) : super();
@override
Widget build(BuildContext context) {
print("redux__second_build");
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: StoreConnectorTextWidget(),
),
StoreConnectorAddButtonWidget("加一", 1),
StoreConnectorAddButtonWidget("加二", 2),
StoreConnectorDecreButtonWidget("减一", 1),
StoreConnectorDecreButtonWidget("减二", 2),
],
),
));
}
}
感兴趣的话,大家可以试试.
下面,我们来分析下Redux实现状态管理的原理:
2.Redux原理
1.StoreProvider
- 根据翻译可以了解到, 它可以向它所有的子类提供Redux[Store],子类中使用[StoreConnector]或[StoreBuilder]拿到到它提供的共享数据.
- 初始化需要传入一个store
- 另外,它是一个inheritedWidget.
内部也给出了一个例子,可以通过其内部提供的of方法获取到store,进而拿到共享数据.
Store<String> store = StoreProvider.of<String>(context, listen: true);
2.Store的创建
- 需要传入一个reducer 、 initialState、middleware中间件函数或列表
reducer: 指定应如何更改状态以响应已调度的操作
initialState: 定义了首次创建存储时存储的状态
middleware: 在action到达reducer之前,拦截action,并可以改变数据
3.StoreConnector
-
converter:可以使用它获取store,获取原理依然是使用StoreProvider.of方法
image.png
image.png
(1) StoreConnector<CountState, int>,通过store.state.count,拿到共享数据<=>ViewModel,返回数据
(2) StoreConnector<CountState, VoidCallback>通过store.dispatch方法,分发action, 并返回VoidCallback
(3)store.dispatch内部实现可以知道,是拿到_dispatchers第0个,通过中间件执行这个action,然后使用给定的[Reducer]将Action应用于共享数据.
image.png
image.png
image.png
4.StoreBuilder
StoreBuilder内部依然使用StoreConnector实现
image.png