前言
fish-redux 是闲鱼App官方出的框架, 作用是解决Flutter之间的状态管理. 常用的状态管理框架有很多, 有谷歌官方推荐的, 也有个人开发者写的,本文主要讲fish_redux.
常用的Flutter状态管理方案:
从0开始认识fish-redux,举个例子
实现方式有很多,
- Android: 获取数字的控件TextView, 调用setText("${count+1}")
- Flutter: 默认实现方式: setState(){"${count+1}" }
Android原生实现方式没性能问题, 调用setText的时候触发onDraw()重绘,但是Flutter调用setState的实现方式,会导致整个根布局重建, 布局对象会全部重新创建,严重影响性能, 如何控制布局的粗粒度,只让需要变化的数字控件布局重建呢?
fish-redux实现方式
1.建一个Flutter项目,由于fish-redux不支持暂不支持Flutter2.0, 现在以1.22.6版本演示, 在pubspec.yaml里添加依赖,,
dependencies:
flutter:
sdk: flutter
fish_redux: 0.3.7
2.下载模板代码插件 FishReduxTemplate
3.新建一个文件夹count, 右键,选择FishReduxTemplate, 生成Page模板代码
4.先对main.dart进行编码,创建页面入口
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'count/page.dart';
void main() {
runApp(MyApp());
}
///路由管理
class RouteConfig {
static const String countPage = 'page/count';
static final AbstractRoutes routes = PageRoutes(
pages: {
///将你的路由名称和页面映射在一起,比如:RouteConfig.homePage : HomePage(),
RouteConfig.countPage: CountPage(),
},
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FishReduxDemo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: RouteConfig.routes.buildPage(RouteConfig.countPage, null), //作为默认页面
onGenerateRoute: (RouteSettings settings) {
// Material页面切换风格
return MaterialPageRoute<Object>(builder: (BuildContext context) {
return RouteConfig.routes.buildPage(settings.name, settings.arguments);
});
});
}
}
- 对page.dart进行编码
import 'package:fish_redux/fish_redux.dart';
import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
/// 页面根布局,随着页面的创建,fish redux框架会默认创建Effect,Reducer对象进行管理整个页面
class CountPage extends Page<CountState, Map<String, dynamic>> {
CountPage()
: super(
initState: initState, // 创建默认的状态对象
effect: buildEffect(), // 创建 Effect对象
reducer: buildReducer(), // 创建 Reducer 对象
view: buildView, // 创建页面布局,这里就是真正要显示的页面
dependencies: Dependencies<CountState>(
adapter: null,
slots: <String, Dependent<CountState>>{},
),
middleware: <Middleware<CountState>>[],
);
}
- 对view.dart进行编码
import 'package:fimber/fimber_base.dart';
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'action.dart';
import 'state.dart';
/// 首页显示的View布局
Widget buildView(CountState state, Dispatch dispatch, ViewService viewService) {
return _bodyWidget(state, dispatch);
}
Widget _bodyWidget(CountState state, Dispatch dispatch) {
return Scaffold(
appBar: AppBar(
title: Text("FishReduxDemo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(state.count.toString(), style: TextStyle(fontSize: 40, color: Colors.red)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
dispatch(CountActionCreator.increaseCount()); // 1.点击事件触发
},
child: Icon(Icons.add),
),
);
}
- 对effect.dart进行编码
import 'package:fimber/fimber_base.dart';
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Effect<CountState> buildEffect() {
// combine:结合 effect:影响,效果
return combineEffects(<Object, Effect<CountState>>{
CountAction.increase: _onIncreaseCount,
});
}
///自增方法
void _onIncreaseCount(Action action, Context<CountState> context) {
// 2.Effect 处理点击事件,此处不处理,直接通过 context把点击事件分发出去
context.dispatch(CountActionCreator.updateCount());
}
- 对reduce.dart进行编码
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Reducer<CountState> buildReducer() {
return asReducer(
<Object, Reducer<CountState>>{
CountAction.updateCount: _updateCount,
},
);
}
/// 通知View层更新界面
CountState _updateCount(CountState state, Action action) {
final CountState newState = state.clone();
newState..count = state.count + 1; // 3.核心逻辑: 把数字加1,然后返回newState对象,此时页面就会刷新
return newState;
}
- 对state.dart进行编码
import 'package:fish_redux/fish_redux.dart';
// state管理每个页面的数据
class CountState implements Cloneable<CountState> {
int count;
@override
CountState clone() {
return CountState()
..count = count;
}
}
CountState initState(Map<String, dynamic> args) {
return CountState()..count = 0;
}
10.对action.dart进行编码
import 'package:fish_redux/fish_redux.dart';
/// 一个页面有多少个事件,都在枚举中定义多少
enum CountAction { updateCount,increase }
// 点击事件处理类,
class CountActionCreator {
/// 触发点击事件,如果需要传递参数可以放在payload字段中,payload是dynamic类型,可传任何类型
static Action increaseCount() {
return Action(CountAction.increase,payload: "事件参数");
}
static Action updateCount() {
return Action(CountAction.updateCount,payload: ["可有可无"]);
}
}
如果你是初学者,相信你此时是一脸懵逼的, what?
原理理解
1. view.dart里按钮的点击事件被触发 dispatch(CountActionCreator.increaseCount());
2.框架会自动调用effect.dart里buildEffect()
配置的事件对应的方法, 然后通过context.dispatch()
继续 把事件分发出去
3. 框架会自动调用reducer.dart里builderReducer()
配置的时间对应的方法.最后更新state里的count, 把state进行return, 框架会对绑定了state里count的布局进行重新创建.
这样就实现了布局的粗粒度控制, 只对需要变化的布局进行重建,提高性能.同时业务逻辑可以写在effect或者reducer里,一般都是写在reducer里.虽然代码多了几倍,但是后续随着业务的复杂,通过这种UI 和 业务逻辑分离的方式可以更好的扩展. 是不是有点像MVC, MVP MVVM 这些模式了? 比如: Android一般在onCreate()里请求接口,请求成功后刷新页面, ,而fish-redux 可以在effect.dart里处理
Effect<CountState> buildEffect() {
// combine:结合 effect:影响,效果
return combineEffects(<Object, Effect<CountState>>{
CountAction.increase: _onIncreaseCount,
Lifecycle.initState: _onCreate,
});
}
void _onCreate(Action action, Context<CountState> context){
// 接口请求..
// 请求成功后,通过context.dispatch把数据传递给reducer
context.dispatch(...);
}
总结
理解fish-redux的原理, 知道哪些方法由框架自动调用,在以后的编码过程中会少走弯路.