简单介绍 闲鱼 fish-redux

870 阅读4分钟

前言

fish-redux 是闲鱼App官方出的框架, 作用是解决Flutter之间的状态管理. 常用的状态管理框架有很多, 有谷歌官方推荐的, 也有个人开发者写的,本文主要讲fish_redux.

常用的Flutter状态管理方案:

从0开始认识fish-redux,举个例子

image.png

实现方式有很多,

  • 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

image.png 3.新建一个文件夹count, 右键,选择FishReduxTemplate, 生成Page模板代码

image.png

image.png

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);
          });
        });
  }
}

  1. 对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>>[],
        );
}

  1. 对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),
    ),
  );
}
  1. 对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());
}
  1. 对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;
}
  1. 对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?

image.png

原理理解

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(...);
}

image.png

总结

理解fish-redux的原理, 知道哪些方法由框架自动调用,在以后的编码过程中会少走弯路.