Fish_redux开发flutter的入门教学
1.Fish_redux的初相识
Fish Redux 是一个基于 Redux 数据管理的组装式 flutter 应用框架, 它特别适用于构建中大型的复杂应用。
它的特点是配置式组装。 一方面我们将一个大的页面,对视图和数据层层拆解为互相独立的 Component|Adapter,上层负责组装,下层负责实现; 另一方面将 Component|Adapter 拆分为 View,Reducer,Effect 等相互独立的上下文无关函数。
所以它会非常干净,易维护,易协作!
我们先看看fish_redux的官方GitHub给出的架构图!
1.1 Fish_redux的组成
- Page
page
代表一个完整的页面,也就是我们理解的activity
或者UIViewController
。它由redux里面分层的view
、state
、reducer
、effect
、action
组成。这就是所谓的组装拔插式...你懂的(#^.^#)
class HomePage extends Page<HomeState, Map<String, dynamic>> {
HomePage()
: super(
initState: initState,//生命周期
effect: buildEffect(),//处理副作用的action
view: buildView,//界面view
dependencies: Dependencies<HomeState>(
adapter: NoneConn<HomeState>() + HomeAdapter(),//高性能的listview适配器
slots: <String, Dependent<HomeState>>{
}),//用到的挂件,也就是组件components
middleware: <Middleware<HomeState>>[
],);//oop切片,可以监听某些方法和生命周期的执行
}
- State
state
主要用来保存page
或者component
(页面/组件)的状态或者说属性,用来存放数据。
class HomeState implements Cloneable<HomeState> {
EasyRefreshController refreshController;
List<HandoverDataModel> list;
int page;
@override//必须复写的方法
HomeState clone() {
return HomeState()
..list = list
..refreshController = refreshController
..page = page;
}
}
//初始化状态,也就是给页面的数据初始化等
HomeState initState(Map<String, dynamic> args) {
HomeState homeState = HomeState();
homeState.refreshController = new EasyRefreshController();
homeState.list = new List();
homeState.page = 1;
return homeState;
}
-
Action
action
就是我们定义的意图,我们把事件或者某些操作定义成意图,通过发送dispatch特定的action,让effect或者reducer里对应的接收者进行处理!
//定义我们的意图,一般为枚举,命名最好是词达意
//Effect 接受处理的 Action,以 on{Verb} 命名
//Reducer 接受处理的 Action,以{verb} 命名
//playload用来约束参数类型
enum HomeAction { onLoad, onRefresh }
//effect的action
class HomeActionCreator {
static Action onLoad(int page) {
return Action(HomeAction.onLoad,payload: page);
}
static Action onRefresh(){
return Action(HomeAction.onRefresh);
}
}
enum HandOverAction { reloadData, loadMoreData }
// reducer的action
class HandOverActionCreator {
static Action reloadData(List<HandoverDataModel> list) {
return Action(HandOverAction.reloadData,payload: list);
}
static Action loadMoreData(List<HandoverDataModel> list) {
return Action(HandOverAction.loadMoreData,payload: list);
}
}
-
Reducer
reducer
主要用来接收某个意图action
,主要用来处理数据方面变化,修改等操作,即更新状态state
。Reducer
是一个上下文无关的 pure function。
它接收下面的参数- T state
- Action action
它主要包含三方面的信心
- 接收一个意图
action
,做出数据修改。 - 如果要修改数据,需要创建一份新的拷贝,修改在拷贝上。
- 如果数据修改了,它会自动触发
State
的层层数据的拷贝,在以扁平化方式通知组件刷新。
Reducer<HomeState> buildReducer() {
return asReducer(
<Object, Reducer<HomeState>>{
HandOverAction.reloadData: _reloadData,
HandOverAction.loadMoreData:_loadMoreData,
},
);
}
HomeState _reloadData(HomeState state, Action action) {
final HomeState newState = state.clone();
// newState.list.clear();
newState.list = action.payload;
return newState;
}
HomeState _loadMoreData(HomeState state, Action action){
final HomeState newState = state.clone();
newState.list.addAll(action.payload);
return newState;
}
-
Effect
effect
的用法跟reducer
类似,也是用来接收某个意图action
,但是它主要用来处理所谓的函数副作用
,简单理解为数据和状态state
更新变化外为的其它操作和事务.默认情况下,effect
会在reducer
之前被执行。
它接收下面的参数- Action action
- Context context
- BuildContext context
- T state
- dispatch
- isDisposed
Effect
会接收来自View
的"意图",包括对应的生命周期的回调,然后做出具体的执行,例如页面跳转,dialog弹出等!它的处理可能是一个异步函数,数据可能在过程中被修改,所以我们应该通过 context.state
获取最新数据。 如果它要修改数据,应该发一个 Action
到 Reducer
里去处理。它对数据是只读的,不能直接去修改数据
Effect<HomeState> buildEffect() {
return combineEffects(<Object, Effect<HomeState>>{
Lifecycle.initState:_init,
HomeAction.onLoad: _onLoad,
HomeAction.onRefresh: _onRefresh,
});
}
void _init(Action action, Context<HomeState> ctx){
ctx.dispatch(HomeActionCreator.onRefresh());
}
void _onLoad(Action action, Context<HomeState> ctx) {
int page = action.payload;
println("onload$page");
NetUtlis.get("/utdcjjb/fdjiaojiebanlog/list.do?filter={'gangweiCode_\$equal':'5'}&page=$page&rows=10&sidx=lrtime&sord=desc",success: (value){
ctx.state.page += 1;
HandoverEntity entity = HandoverEntity.fromJson(value);
ctx.state.refreshController.finishLoad(noMore: entity.data.xList.length <10);
ctx.dispatch(HandOverActionCreator.onLoadMoreData(entity.data.xList));//转发到reducer去处理数据变化
},failure: (error){
print(error);
});
}
void _onRefresh(Action action,Context<HomeState> ctx){
int page = 1;
ctx.state.page = 1;
NetUtlis.get("/utdcjjb/fdjiaojiebanlog/list.do?filter={'gangweiCode_\$equal':'5'}&page=$page&rows=10&sidx=lrtime&sord=desc",success: (value){
ctx.state.page += 1;
HandoverEntity entity = HandoverEntity.fromJson(value);
ctx.state.refreshController.resetLoadState();
ctx.state.refreshController.finishRefresh();
ctx.dispatch(HandOverActionCreator.onReloadData(entity.data.xList));//转发到reducer去处理数据变化
},failure: (error){
print(error);
});
}
-
View
是一个输出Widget的上下文无关的函数。它接收下面的参数- T state
- Dispatch
- ViewService
它主要包含三方面的信息
- 视图完全由数据驱动
- 视图产生的事件/回调,通过Dispatch发出"意图"(action),但绝不做出具体实现。
- 使用依赖的组件/适配器,通过在组件上显示配置(component),再通过
ViewService
标准化调用。其中ViewService
提供了三个能力- BuildContext context,获取flutter Build-Context的能力
- Widget buildView(String name),直接创建子组件的能力
- 这里传入的name即在
Dependencies
上配置的名称。 - 创建的子组件不需要传入任何其他的参数,因为子组件需要的参数,已经通过
Dependencies
配置中将它们的数据关系,通过connector确立。
- 这里传入的name即在
- ListAdapter buildAdapter(),直接创建适配器的能力
Widget buildView(HomeState state, Dispatch dispatch, ViewService viewService) {
final ListAdapter adapter = viewService.buildAdapter();//适配器
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50.0),
child: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
elevation: 0,
title: Text('值长日志'),
),
),
body: SafeArea(
child: EasyRefresh(
// bottomBouncing: false,
// topBouncing: false,
header: ClassicalHeader(),
footer: ClassicalFooter(
enableInfiniteLoad: false,
enableHapticFeedback: false,
),
controller: state.refreshController,
onRefresh: () async{
dispatch(HomeActionCreator.onRefresh());//意图回调
},
onLoad: () async{
dispatch(HomeActionCreator.onLoad(state.page));
},
child: ListView.builder(
itemBuilder: adapter.itemBuilder,
itemCount: adapter.itemCount,
),
),
),
);
}
-
Connector<T,P>
- 它表达了如何从一个大数据中读取小数据,同时对小数据的修改如何同步给大数据,这样的数据连接关系
- 它是将一个集中式的
Reducer
,可以由多层次多模块的小Reducer
自动拼接的关键。- 它大大的降低了我们使用的
Redux
的复杂度。我们不再关心组装过程,我们关心的核心是什么动做促使数据怎么变化。
- 它大大的降低了我们使用的
- 它使用在配置
Dependencies
中,在配置中我们就固化了大组件和小组件之间的连接关系(数据管道),所以在我们使用小组件的时候是不需要传入任何动态参数的。
class _HomeConnector extends ConnOp<HomeState, List<ItemBean>> {
//HomeState是大page的数据,handover是列表item的数据,通过大数据给小数据赋值
@override
List<ItemBean> get(HomeState state) {
if(state.list?.isNotEmpty == true){
return state.list.map<ItemBean>((m){
HandOverState handOverState = new HandOverState();
handOverState.handoverDataModel = m;
return ItemBean("handover", handOverState);
}).toList();
}else{
return<ItemBean>[];
}
}
//小数据发生改变,回传给大数据
@override
void set(HomeState state, List<ItemBean> items) {
if(items.isNotEmpty == true){
state.list = List<HandoverDataModel>.from(items.map((m){
return m;
}).toList());
}else{
state.list = new List();
}
}
@override
subReducer(reducer) {
// TODO: implement subReducer
return super.subReducer(reducer);
}
}
-
Dependencies
是一个表达组件之间依赖关系的结构。它接收两个字段- slots
- <String,Dependent>{}
- adapter
它主要包含三方面的信息
- slots, 组件依赖的插槽
- adapter, 组件依赖的具体适配器(用来构建高性能的ListView)。
- Dependent 是subComponent|subAdapter + connector的组合。
- 一个组件的
Reducer
由Component自身配置的Reducer
和它的Dependencies
下的所有子Reducers
自动复合而成。
- slots
///register in component,真实数据没接入component,所以拷贝的官方数据
class ItemComponent extends ItemComponent<ItemState> {
ItemComponent()
: super(
view: buildItemView,
reducer: buildItemReducer(),
dependencies: Dependencies<ItemState>(
slots: <String, Dependent<ItemState>>{
'appBar': AppBarComponent().asDependent(AppBarConnector()),
'body': ItemBodyComponent().asDependent(ItemBodyConnector()),
'ad_ball': ADBallComponent().asDependent(ADBallConnector()),
'bottomBar': BottomBarComponent().asDependent(BottomBarConnector()),
},
),
);
}
///call in view
Widget buildItemView(ItemState state, Dispatch dispatch, ViewService service) {
return Scaffold(
body: Stack(
children: <Widget>[
service.buildComponent('body'),
service.buildComponent('ad_ball'),
Positioned(
child: service.buildComponent('bottomBar'),
left: 0.0,
bottom: 0.0,
right: 0.0,
height: 100.0,
),
],
),
appBar: AppbarPreferSize(child: service.buildComponent('appBar')));
}
-
Adapter
首先要明白Adapter
是一种组件化的抽象,主要是运用在ListView
的场景,提高性能和流畅度。一个Adapter
和Component
几乎是一致的,除了以下几点-
Component
生成一个Widget
,Adapter
生成一个ListAdapter
,ListAdapter
有能力生成一组Widget
。- 不具体生成
Widget
,而是一个ListAdapter
,能非常大的提升页面帧率和流畅度。
- 不具体生成
-
Effect-Lifecycle-Promote
-
Component
的Effect
是跟着Widget
的生命周期走的,Adapter
的Effect
是跟着上一级的Widget
的生命周期走。 -
Effect
提升,极大的解除了业务逻辑和视图生命的耦合,即使它的展示还未出现,的其他模块依然能通过dispatch-api
,调用它的能力。
-
-
-
appear|disappear
的通知- 由于
Effect
生命周期的提升,我们就能更加精细的区分init|dispose
和appear|disappear
。而这在Component
的模型中是无法区分的
- 由于
Reducer is long-lived, Effect is medium-lived, View is short-lived.
其中Adapter有三种实现,我们需要继承其中一种
- DynamicFlowAdapter(模版是一个 Map,接受一个数组类型的数据驱动)
- StaticFlowAdapter(模版是一个 Array,接受 Object|Map 的数据驱动,模版接收一个 Dependent 的数组,每一个 Dependent 可以是 Component 或者 Adapter + Connector<T,P> 的组合)
- CustomAdapter(对大 Cell 的自定义实现
要素和 Component 类似,不一样的地方是 Adapter 的视图部分返回的是一个 ListAdapter)
-
Lifecycle
默认的所有生命周期,本质上都来自于 flutter State 中的生命周期- initState
- didChangeDependencies
- build
- didUpdateWidget
- deactivate
- dispose
- 在组件内,Reducer 的生命周期是和页面一致的,Effect 和 View 的生命周期是和组件的 Widget 一致的。
- 在适配器中,Reducer 的生命周期是和页面一致的,Effect 的生命周期是和 ListView 的生命周期一致,View 的生命周期是短暂的(划入不可见区域即销毁)。同时增加了 appear 和 disappear 的生命周期, 代表这个 adapter 管理的视图数组,刚进入显示区和完全离开显示区的回调。
我们了解完基础的fish_redux库的一些基本组成了,在这个基础上,我们来看一下用fish_redux编写flutter的目录结构,主要借助于闲鱼团队提供的插件FishReduxTemplate
我们省了很多功夫(剩下的就是命名和自己组合的能力了)
lib//主要编写代码目录
├── app.dart//主入口
├── entity_factory.dart//模型工厂辅助类
├── home//主页面
│ ├── action.dart
│ ├── effect.dart
│ ├── home_adapter//适配器
│ │ ├── action.dart
│ │ ├── adapter.dart
│ │ └── reducer.dart
│ ├── home_component//组件
│ │ ├── action.dart
│ │ ├── component.dart
│ │ ├── effect.dart
│ │ ├── reducer.dart
│ │ ├── state.dart
│ │ └── view.dart
│ ├── page.dart
│ ├── state.dart
│ └── view.dart
├── login//login页面
│ ├── action.dart
│ ├── effect.dart
│ ├── page.dart
│ ├── state.dart
│ └── view.dart
├── main.dart
└── network//网络请求和模型实体类
├── handover_entity.dart
├── netuntil.dart
└── user_info_entity.dart
看完以上我们再来了解一下fish_redux的的工作流程:
整体流程如下:
用户进行某个操作----->然后调用context.dispatch方法发送一个由ActionCreator创建的Action----->effect接收并处理,然后dispatch给reducer----->reducer接收并产生新的state----->state更新导致界面显示的刷新
2.fish_redux实战,接入网络,刷新等等实际应用场景
2.1 创建应用的根Widget
新建一个app.dart用来创建应用的根Widget,app.dart如下:
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_redux_demo/home/page.dart';
import 'package:flutter_redux_demo/login/page.dart';
Widget createApp() {
final AbstractRoutes routes = PageRoutes(
pages: <String, Page<Object, dynamic>>{
/// 注册登陆主页面
"login": LoginPage(),
"home": HomePage(),
},
visitor: (String path, Page<Object, dynamic> page) {
/// AOP
/// 页面可以有一些私有的 AOP 的增强, 但往往会有一些 AOP 是整个应用下,所有页面都会有的。
/// 这些公共的通用 AOP ,通过遍历路由页面的形式统一加入。
page.enhancer.append(
/// View AOP
viewMiddleware: <ViewMiddleware<dynamic>>[
safetyView<dynamic>(),
],
/// Adapter AOP
adapterMiddleware: <AdapterMiddleware<dynamic>>[safetyAdapter<dynamic>()],
/// Effect AOP
effectMiddleware: [
// _pageAnalyticsMiddleware<dynamic>(),
],
/// Store AOP
middleware: <Middleware<dynamic>>[
//这块主要用到middleware的打印功能,监听Action在页面间的调整过程
//logMiddleware<dynamic>(tag: page.runtimeType.toString()), //这块主要用到middleware的打印功能,监听Action在页面间的调整过程
],
);
},
);
return MaterialApp(
title: 'fish_redux_demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: routes.buildPage('login',null),
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<Object>(builder: (BuildContext context) {
return routes.buildPage(settings.name, settings.arguments);
});
},
);
}
2.2新建第一个fish_redux页面Login
首先打开AS,新建一个package命名为login,表示登录页面,然后利用插件FishReduxTemplate,弹出快速创建页面:
2.2.1编写Login-Page.dart,zhuyao
import 'package:fish_redux/fish_redux.dart';
import 'effect.dart';
import 'state.dart';
import 'view.dart';
class LoginPage extends Page<LoginState, Map<String, dynamic>> {
LoginPage()
: super(
initState: initState,
effect: buildEffect(),
view: buildView,
);
}
2.2.2 编写Login-page-state.dart,对应page.dart里面的initState
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/cupertino.dart';
class LoginState implements Cloneable<LoginState> {
TextEditingController userNameController;
TextEditingController passwordController;
@override
LoginState clone() {
return LoginState()
..userNameController = userNameController
..passwordController = passwordController;
}
}
LoginState initState(Map<String, dynamic> args) {
LoginState state = LoginState();
state.userNameController = new TextEditingController();
state.passwordController = new TextEditingController();
return state;
}
2.2.3 编写Login-page-view.dart,对应page.dart里面的buildView
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'action.dart';
import 'state.dart';
Widget buildView(LoginState state, Dispatch dispatch, ViewService viewService) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50.0),
child: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
elevation: 0,
title: Text('登录'),
),
),
body: GestureDetector(
onTap: (){//键盘收起
FocusScope.of(viewService.context).requestFocus(new FocusNode());
},
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 100,),
Text(
"安全管控",
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
SizedBox(height: 100,),
Row(
children: <Widget>[
SizedBox(width: 50,),
Expanded(
child: TextField(
controller: state.userNameController,
decoration: InputDecoration(
hintText: "用户名",
),
),
),
SizedBox(width: 50,),
],
),
SizedBox(height: 30,),
Row(
children: <Widget>[
SizedBox(width: 50,),
Expanded(
child: TextField(
controller: state.passwordController,
decoration: InputDecoration(
hintText: "密码",
),
obscureText: true,
),
),
SizedBox(width: 50,),
],
),
SizedBox(height: 50,),
RaisedButton(
color: Colors.blue,
child: Text(
'登录',
style: TextStyle(
color: Colors.white,
),
),
onPressed: (){
if(state.userNameController.text.isNotEmpty){
if(state.passwordController.text.isNotEmpty){
dispatch(LoginActionCreator.onLogin());
}
else{
Fluttertoast.showToast(msg: '密码不能为空');
}
}
else{
Fluttertoast.showToast(msg: '账号不能为空');
}
},
),
],
),
),
),
);
}
2.2.4 编写Login-page-effect.dart,对应page.dart里面的buildEffect
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/cupertino.dart' hide Action;
import 'package:flutter_redux_demo/network/netuntil.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'action.dart';
import 'state.dart';
import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
Effect<LoginState> buildEffect() {
return combineEffects(<Object, Effect<LoginState>>{
LoginAction.login: _onLogin,
});
}
void _onLogin(Action action, Context<LoginState> ctx) {
Map<String, dynamic> params = {
'username':ctx.state.userNameController.text,
'pwd':generateMd5(ctx.state.passwordController.text),
};
//登录接口
NetUtlis.login('/userright/loginVerify.do',params: params,success: (value){
if(value == '1'){
Fluttertoast.showToast(msg: '登录成功');
Navigator.of(ctx.context).pushNamed('home');
}
else{
Fluttertoast.showToast(msg: '登录失败');
}
});
}
//MD5转码
String generateMd5(String data) {
var content = new Utf8Encoder().convert(data);
var digest = md5.convert(content);
// 这里其实就是 digest.toString()
return hex.encode(digest.bytes);
}
2.2.5 编写Login-page-action.dart,对应effect.dart里面意图action
import 'package:fish_redux/fish_redux.dart';
enum LoginAction { login }
class LoginActionCreator {
static Action onLogin() {
return const Action(LoginAction.login);
}
}
至此,登录页面逻辑基本完成,这里没有用到adapter,dependencies等等.我们进入home页面!
2.3 新建主界面Home,按照上面的插件步骤,建立命名home的package
2.3.1 编写Home-page.dart
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_redux_demo/home/home_adapter/adapter.dart';
import 'effect.dart';
import 'state.dart';
import 'view.dart';
class HomePage extends Page<HomeState, Map<String, dynamic>> {
HomePage()
: super(
initState: initState,
effect: buildEffect(),
view: buildView,
dependencies: Dependencies<HomeState>(
adapter: NoneConn<HomeState>() + HomeAdapter(),
slots: <String, Dependent<HomeState>>{
}),
middleware: <Middleware<HomeState>>[
],);
}
2.3.2 编写Home-page-state.dart,对应page.dart的initState
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_redux_demo/network/handover_entity.dart';
class HomeState implements Cloneable<HomeState> {
EasyRefreshController refreshController;
List<HandoverDataModel> list;
int page;
@override
HomeState clone() {
return HomeState()
..list = list
..refreshController = refreshController
..page = page;
}
}
HomeState initState(Map<String, dynamic> args) {
HomeState homeState = HomeState();
homeState.refreshController = new EasyRefreshController();
homeState.list = new List();
homeState.page = 1;
return homeState;
}
2.3.3 编写Home-page.view.dart,对应page.dart里面的buildView
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'action.dart';
import 'state.dart';
Widget buildView(HomeState state, Dispatch dispatch, ViewService viewService) {
final ListAdapter adapter = viewService.buildAdapter();
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50.0),
child: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
elevation: 0,
title: Text('值长日志'),
),
),
body: SafeArea(
child: EasyRefresh(
// bottomBouncing: false,
// topBouncing: false,
header: ClassicalHeader(),
footer: ClassicalFooter(
enableInfiniteLoad: false,
),
controller: state.refreshController,
onRefresh: () async{
dispatch(HomeActionCreator.onRefresh());
},
onLoad: () async{
dispatch(HomeActionCreator.onLoad(state.page));
},
child: ListView.builder(
itemBuilder: adapter.itemBuilder,
itemCount: adapter.itemCount,
),
),
),
);
}
2.3.4 编写Home-page-action.dart,对应view.datr里面用的action
import 'package:fish_redux/fish_redux.dart';
enum HomeAction { onLoad, onRefresh }
class HomeActionCreator {
static Action onLoad(int page) {
return Action(HomeAction.onLoad,payload: page);
}
static Action onRefresh(){
return Action(HomeAction.onRefresh);
}
}
2.3.5 编写Home-page-effect.dart,对应page.dart里面的buildEffect
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_redux_demo/home/home_adapter/action.dart';
import 'package:flutter_redux_demo/network/handover_entity.dart';
import 'package:flutter_redux_demo/network/netuntil.dart';
import 'action.dart';
import 'state.dart';
Effect<HomeState> buildEffect() {
return combineEffects(<Object, Effect<HomeState>>{
Lifecycle.initState:_init,
HomeAction.onLoad: _onLoad,
HomeAction.onRefresh: _onRefresh,
});
}
void _init(Action action, Context<HomeState> ctx){
ctx.dispatch(HomeActionCreator.onRefresh());
}
void _onLoad(Action action, Context<HomeState> ctx) {
int page = action.payload;
println("onload$page");
NetUtlis.get("/utdcjjb/fdjiaojiebanlog/list.do?filter={'gangweiCode_\$equal':'5'}&page=$page&rows=10&sidx=lrtime&sord=desc",success: (value){
ctx.state.page += 1;
HandoverEntity entity = HandoverEntity.fromJson(value);
ctx.state.refreshController.finishLoad(noMore: entity.data.xList.length <10);
ctx.dispatch(HandOverActionCreator.loadMoreData(entity.data.xList));
},failure: (error){
print(error);
});
}
void _onRefresh(Action action,Context<HomeState> ctx){
int page = 1;
ctx.state.page = 1;
NetUtlis.get("/utdcjjb/fdjiaojiebanlog/list.do?filter={'gangweiCode_\$equal':'5'}&page=$page&rows=10&sidx=lrtime&sord=desc",success: (value){
ctx.state.page += 1;
HandoverEntity entity = HandoverEntity.fromJson(value);
ctx.state.refreshController.resetLoadState();
ctx.state.refreshController.finishRefresh();
ctx.dispatch(HandOverActionCreator.reloadData(entity.data.xList));
},failure: (error){
print(error);
});
}
2.3.6 新建一个home_adapter的package,按照插件格式,生成文件目录,编写Home-adapter.dart,对应page.dart里面dependencies的adapter
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_redux_demo/home/home_component/component.dart';
import 'package:flutter_redux_demo/home/home_component/state.dart';
import 'package:flutter_redux_demo/network/handover_entity.dart';
import 'reducer.dart';
import '../state.dart';
class HomeAdapter extends DynamicFlowAdapter<HomeState> {
HomeAdapter()
: super(
pool: <String, Component<Object>>{
'handover': HandOverComponent(),
},
connector: _HomeConnector(),
reducer: buildReducer(),
);
}
class _HomeConnector extends ConnOp<HomeState, List<ItemBean>> {
@override
List<ItemBean> get(HomeState state) {
if(state.list?.isNotEmpty == true){
return state.list.map<ItemBean>((m){
HandOverState handOverState = new HandOverState();
handOverState.handoverDataModel = m;
return ItemBean("handover", handOverState);
}).toList();
}else{
return<ItemBean>[];
}
}
@override
void set(HomeState state, List<ItemBean> items) {
if(items.isNotEmpty == true){
state.list = List<HandoverDataModel>.from(items.map((m){
return m;
}).toList());
}else{
state.list = new List();
}
}
}
2.3.7 编写adapter的的action.dart和reducer.dart,生命周期比较长
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_redux_demo/network/handover_entity.dart';
enum HandOverAction { reloadData, loadMoreData }
class HandOverActionCreator {
static Action reloadData(List<HandoverDataModel> list) {
return Action(HandOverAction.reloadData,payload: list);
}
static Action loadMoreData(List<HandoverDataModel> list) {
return Action(HandOverAction.loadMoreData,payload: list);
}
}
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import '../state.dart';
Reducer<HomeState> buildReducer() {
return asReducer(
<Object, Reducer<HomeState>>{
HandOverAction.reloadData: _reloadData,
HandOverAction.loadMoreData:_loadMoreData,
},
);
}
//更新数据
HomeState _reloadData(HomeState state, Action action) {
final HomeState newState = state.clone();
// newState.list.clear();
newState.list = action.payload;
return newState;
}
HomeState _loadMoreData(HomeState state, Action action){
final HomeState newState = state.clone();
newState.list.addAll(action.payload);
return newState;
}
2.3.8 新建home_component的package,根据插件提示生成目录结构。编写Home-page-component.dart
import 'package:fish_redux/fish_redux.dart';
import 'effect.dart';
import 'state.dart';
import 'view.dart';
class HandOverComponent extends Component<HandOverState> {
HandOverComponent()
: super(
view: buildView,
);
}
2.3.9 编写Home-page-component-state.dart,保存item的数据
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_redux_demo/network/handover_entity.dart';
class HandOverState implements Cloneable<HandOverState> {
HandoverDataModel handoverDataModel;
@override
HandOverState clone() {
return HandOverState()..handoverDataModel = handoverDataModel;
}
}
2.3.10 因为目前component只有buildView,所以我们只需要编写Home-page-component-view.dart!如果item点击需要进入详情等,就需要编写effect,reducer,action等等
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'package:flustars/flustars.dart';
import 'state.dart';
Widget buildView(HandOverState state, Dispatch dispatch, ViewService viewService) {
return Container(
margin: EdgeInsets.fromLTRB(8, 4, 8, 4),
decoration: BoxDecoration(
border: Border.all(
color: Color(0xffe8ebf0),
width: 1,
),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 5)),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 16),),
Text(
(TextUtil.isEmpty(state.handoverDataModel.zhiciFname)?"当前班" : state.handoverDataModel.zhiciFname) + '-' + state.handoverDataModel.banciFname,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Spacer(
flex: 1,
),
Text(
state.handoverDataModel.statusconfirm == "1"
? "已接班"
: "未接班",
style: TextStyle(
fontSize: 14,
color: state.handoverDataModel.statusconfirm == "1"
? Color(0xFF00CA8D)
: Color(0xFFFF9B26),
),
),
Padding(padding: EdgeInsets.only(right: 16),),
],
),
Padding(padding: EdgeInsets.only(top: 2)),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 16),),
Text(
"当班值长 :" + state.handoverDataModel.dbpeople,
style: TextStyle(
fontSize: 14,
color: Color(0xFF676767),
),
),
],
),
Padding(padding: EdgeInsets.only(top: 2)),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 16),),
Text(
"接班时间 :" + state.handoverDataModel.mdate +" " +
state.handoverDataModel.jbtime,
style: TextStyle(
fontSize: 14,
color: Color(0xFF676767),
),
)
],
),
Padding(padding: EdgeInsets.only(bottom: 5)),
],
),
);
}
至此,Home的页面也编写完成,逻辑也串起来了!
让我们来看看预览画面