Home fish-redux库的学习使用
Post
Cancel

fish-redux库的学习使用

fish-redux库的介绍

fish-redux是一个基于 Redux 数据管理的组装式 flutter 应用框架。现已由阿里闲鱼团队开源,项目地址:Fish Redux

关于该库,官方也已经有了详细的介绍:刚刚,阿里宣布开源Flutter应用框架Fish Redux!

但是,如果对Redux不太了解,或者刚接触Fish Redux不久, 肯定会有很多困惑、茫然,不知如何使用。因为相较于flutter-redux, fish-redux在它的基础上又抽象出了很多的概念,如EffectAdapterDependentPage等。想短时间内完全理解,确实很困难。这也是本文的目的,帮助我们更快的理解使用fish-redex


fish-redux 的关系图

针对这么多的概念,我是通过画图的方式来加深理解的。

Fish-Redux关系图

从图中可以看出,fish-redux大大小小的概念快20个了。下面详细介绍每个概念的作用以及彼此的关系。


fish-redux的详细介绍

Page<T, P>

T指状态的数据类型,P指页面的入参类型。

Page就是一个完整的页面,它继承自Component<T>。同时扩展出了两个参数:InitState<T, P>Middleware<T>

middleware的概念与Redux中是一致的。

initState的作用是将打开页面时传入的参数类型为P的值对应到状态T中,控制页面初始的显示内容。

如何打开一个页面?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//创建一个Page
class MyPage extends Page<PageState, List<String>> {
    ....
}

void main() => runApp(createApp());

Widget createApp() {
  //将page添加到routes
  final AbstractRoutes routes = HybridRoutes(routes: [
    PageRoutes(
      pages: <String, Page<Object, dynamic>> {
        "home_page": MyPage()
      }
    )
  ]);

  return MaterialApp(
    title: "Test fish redux",
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    //通过routes.buildPage的方式生成一个Widget
    home: routes.buildPage("home_page", ["android", "iOS", "flutter", "java", "kotlin", "dart"]),
    onGenerateRoute: (settings) {
      return MaterialPageRoute<Object>(builder: (context) {
        return routes.buildPage(settings.name, settings.arguments);
      });
    },
  );
}

从上面的例子可以看出,通过AbstractRoutes.buildPage(String key, dynamic arguments)来生成页面对应的Widget。而页面传参就是通过第二个参数来传递的。

Component<T>

Page的大部分功能都是Component提供的。Component字面意思就是组件,很明显它是为了控件的复用而存在的。它的特点有:复用、低耦合、即插即用等。

组件的参数比较多,下面逐个介绍。

ViewBuilder<T> view

首先看下ViewBuilder的定义:

1
2
3
4
5
typedef ViewBuilder<T> = Widget Function(
  T state,
  Dispatch dispatch,
  ViewService viewService,
);

它的作用是提供组件的UI展示。state存储着当前的组件状态,dispatch用来发射事件,viewService用来绑定子组件或者adapter

关于viewService,后面有例子帮助理解。

Reducer<T> reducer

reducer的定义:

1
typedef Reducer<T> = T Function(T state, Action action);

Action是框架提供的固定类型,定义如下:

1
2
3
4
5
class Action {
  const Action(this.type, {this.payload});
  final Object type;
  final dynamic payload;
}

reducer的作用就不在详述了。

ReducerFilter<T> filter

filter的定义:

1
typedef ReducerFilter<T> = bool Function(T state, Action action);

它的作用是为组件过滤不必要的事件。只有返回值是trueaction会进入到reducer处理。

Effect<T> effect

effect的定义:

1
typedef Effect<T> = dynamic Function(Action action, Context<T> ctx);

ctx提供组件的上下文,包括了statedispatchBuildContext context,以及appBroadcastpageBroadcast action。

它与reducer有点像,都是处理特定的action事件。区别有以下几点:

  • effect处理非修改数据的行为事件,reducer处理修改数据的行为事件。
  • effect返回boolFuture, reducer无返回值

如果effect的返回值是一个非空值(not null , not false),则代表自己优先处理,不再做下一步的动作;否则广播给其他组件的 Effect 部分,同时发送给 Reducer。

约定effect接收的action.typeon开头,reducer的以非on开头。

看源码,是将effect转成middleware, 如果返回值满足条件,就不在next了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Dispatch createDispatch(
      OnAction onAction, Context<T> ctx, Dispatch parentDispatch) {
    Dispatch dispatch = (Action action) {
      throw Exception(
          'Dispatching while appending your effect & onError to dispatch is not allowed.');
    };

    /// attach to store.dispatch
    dispatch = _applyOnAction<T>(onAction, ctx)(
      dispatch: (Action action) => dispatch(action),
      getState: () => ctx.state,
    )(parentDispatch);
    return dispatch;
  }

static Middleware<T> _applyOnAction<T>(OnAction onAction, Context<T> ctx) {
    return ({Dispatch dispatch, Get<T> getState}) {
      return (Dispatch next) {
        return (Action action) {
          final Object result = onAction?.call(action);
          if (result != null && result != false) {
            return;
          }

          //skip-lifecycle-actions
          if (action.type is Lifecycle) {
            return;
          }

          if (!shouldBeInterruptedBeforeReducer(action)) {
            ctx.pageBroadcast(action);
          }

          next(action);
        };
      };
    };
  }
HigherEffect<T> higherEffect

higherEffecteffect的升级版,它允许拥有自己的临时状态。

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class PageEffectPart extends EffectPart<PageState> {

  //higher effect的状态变量
  bool addFlag = true;

  @override
  Map<Object, OnAction> createMap() {
    return {
      PageType.OnAuto: _pageEffect
    };
  }

  void _pageEffect(Action action) {
    print("at higher effect: ${action.type}");

    if (addFlag) {
      dispatch(ActionCreator.addData());
    } else {
      dispatch(ActionCreator.deleteData());
    }
    addFlag = !addFlag;
  }

}


class MyPage extends Page<PageState, List<String>> {
  MyPage()
      : super(
            ...
            higherEffect: higherEffect(() => PageEffectPart()),
        	...
}

higherEffecteffect同时只能设置其中一个。

OnError<T> onError

onError的定义:

1
typedef OnError<T> = bool Function(Exception exception, Context<T> ctx);

它用来处理effect中的异常。返回true,代表已处理,返回false代表不处理,继续抛出该异常。

Dependencies<T> dependencies

它的作用是给当前组件提供其他组件或者Adapter

dependencies接收两个入参:Map<String, Dependent<T>> slotsAbstractAdapter<T> adapter

slots的作用是给ViewBuilder提供子组件,在ViewBuilder中通过ViewService.buildComponent(key)来使用,key值就是Map的key

Dependent = Connnector + Component。因为子组件的状态与当前组件的状态,类型不一致,需要使用connector来转换沟通。

如:

1
2
3
4
5
6
7
8
9
10
11
class MyPage extends Page<PageState, List<String>> {
  MyPage()
      : super(
          	//...
            dependencies: Dependencies(
                slots: {
                  "count": CountConnector() + CountComponent()
                }
            ),
            //...
}

adapter的作用是给组件中ListView提供ListAdapter的。在ViewBuilder中通过ViewService.buildAdapter()来获取ListAdapter

ShouldUpdate<T> shouldUpdate

shouldUpdate的定义:

1
typedef ShouldUpdate<T> = bool Function(T old, T now);

它的作用是决定组件是否需要重新更新。

通过比较oldnow两种状态,默认逻辑是:若两个变量不相等就刷新。

1
2
static ShouldUpdate<K> updateByDefault<K>() =>
      (K _, K __) => !identical(_, __);
WidgetWrapper wrapper

wrapper的定义:

1
typedef WidgetWrapper = Widget Function(Widget child);

它的作用是包裹ViewBuilder的内容。

List<Middleware<T>> middleware

middleware的定义:

1
2
3
4
typedef Middleware<T> = Composable<Dispatch> Function({
  Dispatch dispatch,
  Get<T> getState,
});

Composable的定义:

1
typedef Composable<T> = T Function(T next);

如何定义一个middleware:

1
2
3
4
5
6
7
8
Composable<Dispatch> pageMiddleware({Dispatch dispatch, Get<PageState> getState}) {
  return (Dispatch next) {
    return (Action action) {
      //do something
      next(action);
    };
  };
}

发射事件的处理流程依次是:effect -> middleware -> reducer


fish-redux的项目结构

推荐的项目结构:

  • main.dart

  • page-1-package
    • page.dart
    • action.dart
    • reducer.dart
    • effect.dart
    • middleware.dart
    • view.dart
    • state.dart
    • adapter-package
    • component-package
  • page-2-package
This post is licensed under CC BY 4.0 by the author.