前面两篇,咱们了解了fish-redux并实现了简单的功能,此次咱们再了解fish-redux一些其余的特色。看一下结果图:github
页面建立的过程跟以前同样,就省略啦。我建立了名为 List 的页面,结果以下 redux
在 app.dart 中加入咱们的这个页面。修改过的 app.dart 以下markdown
Widget createApp() {
final AbstractRoutes routes = PageRoutes(
pages: <String, Page<Object, dynamic>>{
'entrance_page': EntrancePage(),
'grid_page': GridPage(),
'list_page':ListPage(), //此处加入咱们新建的页面
},
);
//省略...
复制代码
而后实现从 Grid 页面跳转到这个页面,(页面跳转上一篇讲过了,这里不详细说。就是建立action、在view中dispatch action、effect中接收到action并跳转页面)代码以下 并发
而后咱们在 list 包下面建立一个 Component 做为列表的每一个 item 。app
首先建立一个名为 item 的包,而后在 item 下建立FishReduxTemplate,此次咱们选择 Component。 框架
建立结果以下,能够看到组件中的 component.dart 相似页面中的 page.dart。 ide
咱们给 state 三个字段 type(图标的形状),title(标题),content(内容)。修改 /list/item/state.dart 以下post
import 'package:fish_redux/fish_redux.dart';
class ItemState implements Cloneable<ItemState> {
int type;
String title;
String content;
ItemState({this.type, this.title, this.content});
@override
ItemState clone() {
return ItemState()
..type = type
..title = title
..content = content;
}
}
ItemState initState(Map<String, dynamic> args) {
return ItemState();
}
复制代码
而后咱们来实现 item 的视图,使用上面 state 的数据,而且根据 type 不一样显示不一样的 icon 图标(详见注释)。/list/item/view.dart 以下ui
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'action.dart';
import 'state.dart';
Widget buildView(ItemState state, Dispatch dispatch, ViewService viewService) {
return Container(
margin: EdgeInsets.fromLTRB(0, 0, 0, 10),
height: 120.0,
color: Colors.white,
child: GestureDetector(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//左侧图标
Container(
padding: const EdgeInsets.only(right: 5.0),
child: Center(
child: Icon(
//不一样type显示不一样icon
state.type == 1 ? Icons.account_circle : Icons.account_box,
size: 50.0,
),
),
),
//右侧
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//标题部分
Container(
height: 30,
child: Text(
state.title,
style: TextStyle(fontSize: 22.0),
),
),
//内容部分
Text(
state.content,
style: TextStyle(fontSize: 16.0),
),
],
),
),
],
),
onTap: () {
//todo 点击事件
},
),
);
}
复制代码
Component告一段落。接着咱们在列表中使用组件。
首先在 List 的 State 中存储每个 Item 的 State 数据。/list/state.dart/ 以下
import 'package:fish_demo/list/item/state.dart';
import 'package:fish_redux/fish_redux.dart';
class ListState implements Cloneable<ListState> {
List<ItemState> items; //保存item的state
@override
ListState clone() {
return ListState()
..items = items;
}
}
ListState initState(Map<String, dynamic> args) {
return ListState();
}
复制代码
而后把 list 和 item 关联,咱们使用 fish-redux 的一个组件叫 adapter。首先在 list 包下用插件建立 FishReduxTemplate,而后选中 DynamicFlowAdapter,并取消其余的勾选,取名 List,以下
看到默认的 adapter 代码中为咱们准备了两个类:一个 ListAdapter 和一个 _ListConnector。其中 ListAdapter 中咱们配置须要关联的组件,即把组件添加到 pool。以及列表和组件的数据适配关系,经过实现一个 connector 。完成的 /list/adapter.dart 以下
import 'package:fish_demo/list/state.dart';
import 'package:fish_redux/fish_redux.dart';
import 'item/component.dart';
import 'item/state.dart';
class ListAdapter extends DynamicFlowAdapter<ListState> {
ListAdapter()
: super(
pool: <String, Component<Object>>{
"MyItem": ItemComponent(), //引用组件
},
connector: _ListConnector(),
);
}
class _ListConnector extends ConnOp<ListState, List<ItemBean>> {
@override
List<ItemBean> get(ListState state) {
//判断ListState里面的items数据是否为空
if (state.items?.isNotEmpty == true) {
//若不为空,把item数据转化成ItemBean的列表
return state.items
.map<ItemBean>((ItemState data) => ItemBean('MyItem', data))
.toList(growable: true);
}else{
//若为空,返回空列表
return <ItemBean>[];
}
}
@override
void set(ListState state, List<ItemBean> items) {
//把ItemBean的变化,修改到item的state的过程
if (items?.isNotEmpty == true) {
state.items = List<ItemState>.from(
items.map<ItemState>((ItemBean bean) => bean.data).toList());
} else {
state.items = <ItemState>[];
}
}
@override
subReducer(reducer) {
// TODO: implement subReducer
return super.subReducer(reducer);
}
}
复制代码
咱们打开 List 页面的 page 文件,而后吧adapter添加到页面的 dependencies 中。以下
import 'package:fish_demo/list/adapter.dart';//注意1
import 'package:fish_redux/fish_redux.dart' hide ListAdapter;//注意1
import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
class ListPage extends Page<ListState, Map<String, dynamic>> {
ListPage()
: super(
initState: initState,
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<ListState>(
adapter: NoneConn<ListState>() + ListAdapter(),//注意2
slots: <String, Dependent<ListState>>{
}),
middleware: <Middleware<ListState>>[
],);
}
复制代码
上面的代码我标注了两点注意。
注意1:咱们这里引用的是刚才自定义的ListAdapter而不是fish-redux自带的ListAdapter类,这里要处理一下引用。(早知道就起名字的时候就不叫ListAdapter了。。。)
注意2:这里使用的"加号"是 fish-redux 重载的操做符。后者是咱们自定义的 adapter ,而因为咱们在外层已经不须要使用 connector,因此前者传入一个 NoneConn 。
最后咱们在 /list/view.dart 获取到 adapter 并完成列表页的UI。/list/view.dart 以下
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'state.dart';
Widget buildView(ListState state, Dispatch dispatch, ViewService viewService) {
ListAdapter adapter = viewService.buildAdapter(); //建立adapter
return Scaffold(
appBar: new AppBar(
title: new Text('列表页'),
),
body: Container(
child: ListView.builder(
itemBuilder: adapter.itemBuilder, //把adapter配置到list
itemCount: adapter.itemCount, //
),
));
}
复制代码
最后咱们给 List 页面配上一些假数据来看看效果。具体过程咱们已经学过了
你们基本都会了,要注意一点此次 action 携带了参数。贴一下代码
最后运行一下看看效果
此次咱们来接触一下 store。它负责管理全局的状态,咱们以主题颜色为例进行演示。
以前页面和组件的建立都是经过插件模板,store 此次手动建立。 建立名为 store 的 package。而后建立一个 state.dart,咱们用它来保存全局状态。本例中就是保存主题颜色。/store/state.dart 以下
import 'dart:ui';
import 'package:fish_redux/fish_redux.dart';
abstract class GlobalBaseState {
Color get themeColor;
set themeColor(Color color);
}
class GlobalState implements GlobalBaseState, Cloneable<GlobalState> {
@override
Color themeColor;
@override
GlobalState clone() {
return GlobalState();
}
}
复制代码
注意这里咱们先定义了一个抽象类 GlobalBaseState 包含 themeColor 字段。一下子咱们会让全部页面的 state 继承它。另外上面咱们还写了一个它的实现类 GlobalState,即全局的 state。
咱们再新建一个 store.dart,做为 App的Store。/store/store.dart 以下
import 'package:fish_redux/fish_redux.dart';
import 'state.dart';
class GlobalStore {
static Store<GlobalState> _globalStore;
static Store<GlobalState> get store =>
_globalStore ??= createStore<GlobalState>(GlobalState(), buildReducer());
}
复制代码
能够看到 store 保存了全局 state,而且它也能拥有 reducer 来处理事件。接下来咱们就定义它的 action 和 reducer。
建立 action.dart ,定义一个改变主题颜色的事件。/store/action.dart 以下
import 'package:fish_redux/fish_redux.dart';
enum GlobalAction { changeThemeColor }
class GlobalActionCreator {
static Action onchangeThemeColor() {
return const Action(GlobalAction.changeThemeColor);
}
}
复制代码
建立 reducer.dart,接收事件并修改 GlobalState 的主题色。(这里咱们让主题色在蓝色和绿色中来回切换)/store/reducer.dart 以下
import 'dart:ui';
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart' hide Action;
import 'action.dart';
import 'state.dart';
Reducer<GlobalState> buildReducer() {
return asReducer(
<Object, Reducer<GlobalState>>{
GlobalAction.changeThemeColor: _onchangeThemeColor,
},
);
}
GlobalState _onchangeThemeColor(GlobalState state, Action action) {
final Color color =
state.themeColor == Colors.green ? Colors.blue : Colors.green;
return state.clone()..themeColor = color;
}
复制代码
这样整个 store 就写好了。全家福
咱们让全部页面的 state 继承 GlobalBaseState,并在页面视图中吧标题栏的颜色设置为 themeColor。咱们一共有三个界面(entrance、grid、list)须要修改,以 list 页面为例,首先是 /list/state.dart
而后是 /list/view.dart
其余两个页面同理,不一一展现了。
如何把各个页面的 state 和全局 GlobalState 联系起来呢?咱们要在 app.dart 中配置 visitor。在这里,咱们判断页面是否继承了 GlobalBaseState,而后跟全局 store 创建联系,即该页面的 state 随全局state更新而更新。修改好的 app.dart以下
Widget createApp() {
final AbstractRoutes routes = PageRoutes(
pages: <String, Page<Object, dynamic>>{
'entrance_page': EntrancePage(),
'grid_page': GridPage(),
'list_page': ListPage(),
},
visitor: (String path, Page<Object, dynamic> page) {
/// 知足条件 Page<T> ,T 是 GlobalBaseState 的子类。
if (page.isTypeof<GlobalBaseState>()) {
/// 创建 AppStore 驱动 PageStore 的单向数据链接
/// 1. 参数1 AppStore
/// 2. 参数2 当 AppStore.state 变化时, PageStore.state 该如何变化
page.connectExtraStore<GlobalState>(
GlobalStore.store, (Object pagestate, GlobalState appState) {
final GlobalBaseState p = pagestate;
if (p.themeColor != appState.themeColor) {
if (pagestate is Cloneable) {
final Object copy = pagestate.clone();
final GlobalBaseState newState = copy;
newState.themeColor = appState.themeColor;
return newState;
}
}
return pagestate;
});
}
},
);
//如下省略...
}
复制代码
最后我但愿经过点击 List 页面的 item,来实现主题色的切换。
首先咱们定义一个 action,/list/item/action.dart 以下
import 'package:fish_redux/fish_redux.dart';
enum ItemAction { action, onThemeChange }
class ItemActionCreator {
static Action onAction() {
return const Action(ItemAction.action);
}
static Action onThemeChange() {
return const Action(ItemAction.onThemeChange);
}
}
复制代码
咱们在 item 点击时,发送这个事件。/list/item/view.dart 以下
Widget buildView(ItemState state, Dispatch dispatch, ViewService viewService) {
return Container(
margin: EdgeInsets.fromLTRB(0, 0, 0, 10),
height: 120.0,
color: Colors.white,
child: GestureDetector(
child: Row(
//省略...
),
onTap: () {
dispatch(ItemActionCreator.onThemeChange());
},
),
);
}
复制代码
在 effect 接收事件,并发送咱们以前定义的全局修改主题色事件。/list/item/effect.dart 以下
import 'package:fish_demo/store/action.dart';
import 'package:fish_demo/store/store.dart';
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Effect<ItemState> buildEffect() {
return combineEffects(<Object, Effect<ItemState>>{
ItemAction.action: _onAction,
ItemAction.onThemeChange: _onThemeChange,
});
}
void _onAction(Action action, Context<ItemState> ctx) {
}
void _onThemeChange(Action action, Context<ItemState> ctx) {
GlobalStore.store.dispatch(GlobalActionCreator.onchangeThemeColor());
}
复制代码
最后运行看下效果: 一步步进入列表页,屡次点击item,主题色随之变化。返回上级页面,主题色也被改变。
至此,咱们了解了 fish-redux 的一些基本的使用。因为客户端同窗对 redux 类框架接触很少,因此在使用时要转变思路、多看文档、多思考。
🤗若是个人内容对您有帮助,欢迎点赞、评论、转发、收藏。