An assembled flutter application framework.

Last update: Aug 10, 2022

Fish Redux

Build Status pub package codecov

What is Fish Redux ?

Fish Redux is an assembled flutter application framework based on Redux state management. It is suitable for building medium and large applications.

It has four characteristics:

  1. Functional Programming
  1. Predictable state container
  1. Pluggable componentization
  1. Non-destructive performance

Architecture diagram

Installation

Go

Documentation

Language: English | 中文简体

Examples

  • todo list - a simple todo list demo.
  • run it:
cd ./example
flutter create .
flutter run

What's the difference between 'Fish Redux' and 'Redux' ?

Plugins

Code Template

Dev-Tools

License

  • Fish Redux is released under the Apache 2.0 license. See LICENSE for details.

关于我们

阿里巴巴-闲鱼技术是国内最早也是最大规模线上运行Flutter的团队。

我们在公众号中为你精选了Flutter独家干货,全面而深入。

内容包括:Flutter的接入、规模化应用、引擎探秘、工程体系、创新技术等教程和开源信息。

架构/服务端/客户端/前端/质量工程师 在公众号中投递简历,名额不限哦

欢迎来闲鱼做一个好奇、幸福、有影响力的程序员,简历投递:[email protected]

订阅地址

For English

GitHub

https://github.com/alibaba/fish-redux
Comments
  • 1. 关于fish-redux支持 App级别Store的讨论

    目前这个特性已经在feature/routes分支上了。参考代码

    1. 在保持原有的设计和实现的基础上,加了很薄的routes层。 routes 仅提供如下能力
    /// Define a basic behavior of routes.
    abstract class AbstractRoutes {
      Widget buildPage(String path, Map<String, dynamic> map);
    }
    
    1. 为原有的Page做了一个routes:PageRoutes -Each page has a unique store.

    2. 为基于一个store的多页面做了一个routes: AppRoutes。 -Multi-page(a route component is a page) sharing a store.

    3. 提供了一个可以装载多个routes的HybridRoutes。

    示例:

    /// How to define ?
    ///     MainRoutes extends HybridRoutes {
    ///       MainRoutes():super(
    ///           routes: [
    ///             PageRoutes(
    ///               pages: <String, Page<Object, Map<String, dynamic>>>{
    ///                 'home': HomePage(),
    ///                 'detail': DetailPage(),
    ///               },
    ///             ),
    ///             AppRoutes<T>(
    ///               preloadedState: T(),
    ///               middleware:[],
    ///               pages: {
    ///                 'message': MsgConn() + MessageComponent(),
    ///                 'personal': PersonalConn() + PersonalComponent(),
    ///               },
    ///             ),
    ///           ]
    ///         );
    ///     }
    ///
    /// How to use ?
    ///     const Routes mainRoutes = MainRoutes();
    ///     mainRoutes.buildPage('home', {});
    ///     mainRoutes.buildPage('message', {});
    

    然后这是大家想要的么?欢迎讨论。

    Reviewed by zjuwjf at 2019-04-02 05:00
  • 2. 收集大家对fish-redux后续演进的期望

    👏大家在这里发声,讨论。

    1. 拓展: 大家想要fish-redux做哪些能力拓展,水平的,垂直的?

    2. 工具: 大家对工具和插件的看法?

    3. 问题: fish-redux在大家使用中会遇到哪些问题 ? 哪些是目前大家使用flutter的普遍的痛点 ?

    4. 未来: 期望fish-redux成为一个什么样的开源产品

    Reviewed by zjuwjf at 2019-03-21 09:56
  • 3. action如何修改当前页面的reducer

    请问在view里面dispatch action 和在effect里面dispatch action有没有区别, 我的理解是当前页面的的state来自于被绑定的connector, 而当前页面的state能够在当前页面的reducer里面被修改状态, 为啥我在view里面派发了action不能起到修改当前页面state的作用?

    Reviewed by jefferybai at 2019-03-22 15:15
  • 4. 配合 built value 创建 state 合理吗?

    手动组装 state 的 clone 有点麻烦,都是重复操作。 所以尝试了下 built value,配合 IDE 的 snippet,感觉方便很多,同时也更大限度的保证了 state Immutable,就是感觉 clone 好像有点多余。 这样做,有什么隐患吗?或者说有什么需要注意的?

    题外话,我记得公众号提到过 fish-serializable ? 有时间计划么,很期待。

    Reviewed by TshineZheng at 2019-03-24 06:15
  • 5. 所有component都依赖的global state的reselect方法

    现在有一个全局对象,比如User。所有的component都有可能用到,并且都会随它的修改而更新。按照fish-redux的设计,我目前是使用reselect,但意味着从page开始到所有的component的state,都需要包裹一层user。虽然我目前是用通用mixin,但是每个state都不得不包裹一层,哪怕自己没用到,子component也有可能用到,还得一层层reselect下去,从性能和设计考虑,都显得实在太臃肿了。 这个现在有什么好的解决方案?或者对框架有所修改,比如加入订阅机制,component可以订阅某个修改。或者换种方式,将connectExtraStore下移到component,使用类似的数据流。

    Reviewed by ykrank at 2020-01-13 07:15
  • 6. TabBarView中使用Adapter的若干问题

    问题描述 TabBarView是由对应的ListView构建而成,每个ListView中的Item实际上又由不同的component通过Abdapter动态构建,在使用过程中有如下问题:

    • 当切换非连续的tab时,会报以下错误 type 'DiscoveryItemState' is not a subtype of type 'DesignItemState' image
    • 已添加KeepAliveWidget来解决Listview来保持页面状态,但是无效,依然会BuildItem。

    数据结构

    enum ItemType {
      none,
      design,
      recruit,
      discovery,
    }
    
    class ItemModel {
    ItemType type = ItemType.none;
    }
    abstract class GlobalItem<T extends GlobalItem<T>> extends Cloneable<T> {
      ItemModel item;
    }
    
    class RecruitItemState implements GlobalItem<RecruitItemState> {
      ItemModel item = new ItemModel()..type = ItemType.recruit;
    }
    
    class DiscoveryItemState implements GlobalItem<DiscoveryItemState> {
      ItemModel item = new ItemModel()..type = ItemType.discovery;
    }
    
    class DesignItemState implements GlobalItem<DesignItemState> {
      ItemModel item = new ItemModel()..type = ItemType.design;
    }
    
    

    Adapter代码

    class HomeListItemAdapter extends DynamicFlowAdapter<HomeState> {
      HomeListItemAdapter()
          : super(
              pool: <String, Component<Object>>{
                'DiscoveryItem': DiscoveryItemComponent(),
                'RecruitItem': RecruitItemComponent(),
                'DesignItem': DesignItemComponent(),
              },
              connector: _HomeListItemConnector(),
            );
    }
    
    class _HomeListItemConnector extends ConnOp<HomeState, List<ItemBean>> {
      @override
      List<ItemBean> get(HomeState state) {
        // List<List<Object>> tabList;   // Object是GlobalItem的子类
        // Map<int, String> tabs; // tabs会动态从服务器中获取,其中Key决定了tabList对应要获取的内容
        if (state.tabs?.isNotEmpty == true && state.tabList?.isNotEmpty == true) {
          return state.tabList[state.currentIndex].map<ItemBean>((itemState) {
            println(itemState.toString());
            String itemType;
            // 根据state中存储的的type类型动态生成Componet
            switch ((itemState as GlobalItem).item.type) {
              case ItemType.design:
                itemType = 'DesignItem';
                break;
              case ItemType.recruit:
                itemType = 'RecruitItem';
                break;
              case ItemType.discovery:
                itemType = 'DiscoveryItem';
                break;
              default:
                itemType = 'DesignItem';
                break;
            }
            if (itemType.isNotEmpty) {
              return ItemBean(itemType, itemState);
            } else {
              return null;
            }
          }).toList();
        } else {
          return <ItemBean>[];
        }
      }
    }
    

    View使用

    Widget buildView(HomeState state, Dispatch dispatch, ViewService viewService) {
      final adapter = viewService.buildAdapter();
      println("适配器的数量:${adapter.itemCount}");
      return Scaffold(
    //    resizeToAvoidBottomPadding: false,
        backgroundColor: Color(0xFFF5F5F5),
        body: Container(
          height: 1000,
          child: CustomScrollView(
            controller: state.customScrollViewController,
            slivers: <Widget>[
              _buildSliverAppBar(state, viewService),
              SliverList(
                delegate: SliverChildListDelegate(
                  [
                    _buildMyApplication(),
                    Container(
                      padding: EdgeInsets.only(
                          top: 15, left: _leftAndRight, right: _leftAndRight),
                      child: viewService.buildComponent('middleBanner'),
                    ),
                    _buildHotTopic(state),
                    Container(
                      padding: EdgeInsets.only(top: 32),
                      child: state.tabController == null
                          ? LoadingView()
                          : _buildTabBar(
                              state,
                              dispatch,
                              viewService.context,
                            ),
                    ),
                  ],
                ),
              ),
              SliverFillRemaining(
                child: Container(
                  child: state.tabController == null
                      ? LoadingView()
                      : TabBarView(
                          // physics: NeverScrollableScrollPhysics(), // 禁滑动事件
                          controller: state.tabController,
                          // *******异常*********
                          children: state.tabs.keys.map((v) {
                            return KeepAliveWidget(
                              Container(
                                child: ListView.builder(
    //                          controller: ,TODO: 用于控制列表被拉上去了,禁止滑动事件
                                  itemBuilder: adapter.itemBuilder,
                                  itemCount: adapter.itemCount,
                                ),
                              ),
                            );
                          }).toList(),
                        ),
                ),
              )
            ],
          ),
        ),
      );
    }
    

    期望

    1. TabbarView中的每个ListView中展现的Item可以不一样
    2. 每个ListView中可以有不同Item
    3. 能正确保持页面状态,能正确切换
    Reviewed by jing-pei at 2019-11-21 03:20
  • 7. 能否在是用list adapter时,子component的action只发送到相应的子reducer?

    能否在是用list adapter时,子component的action只发送到相应的子reducer?比官方示例中的 todo_componnet中的reducer,在一个todo项目上点击了完成,所有的todo state都会受到这个action,然后各个子component的reducer里都要判断被操作的todoState是不是本component的todoState。

    ToDoState _edit(ToDoState state, Action action) {
      final ToDoState toDo = action.payload;
      if (state.uniqueId == toDo.uniqueId) {
        return state.clone()
          ..title = toDo.title
          ..desc = toDo.desc;
      }
      return state;
    }
    
    ToDoState _markDone(ToDoState state, Action action) {
      final String uniqueId = action.payload;
      if (state.uniqueId == uniqueId) {
        return state.clone()..isDone = !state.isDone;
      }
      return state;
    }
    

    这在简单的情况下,还好处理,但是如果 list adapter中的子component 包含下一级component就,这个下一级的component的reducer就会失去独立性,需要祖父component的信息来判断是否要更新本component的state。

    Reviewed by danieldai at 2019-03-28 13:03
  • 8. component 无法更新的问题

    Home page有两个组件component A和component B,component B里有自己的网络请求,请求成功后执行了reducer,数据已经放在component B的state里,但是component B的state在conn里可能被重新赋值为null,造成component B的view重新执行buildView的时候这个值还是null的,这个该如何操作,也就是component B里的数据来自于自己的网络请求,不是来自自于home Page,我该如何获取到正确的数据呢

    Reviewed by hasonguo at 2020-03-26 05:08
  • 9. TodoEditPage中每次修改编辑框内容,都会重新buildView(),这样难道不会影响性能吗?

    如题,这段逻辑很奇怪,我在此页面修改编辑框内容,view层的nameEditController会在Effect中监听处理,最终在Reducer中修改状态,刷新回View 但是奇怪的是:我把编辑框内容改为1,此时view层的编辑框内容已经被我修改过了,为什么还要绕这么一大圈,最后又通知回view,执行buildView(),这样我每次修改编辑框内容,都会反复执行,并且都毫无意义,因为编辑框内容肯定已经是最新的了。 请问这样做有什么意义?

    Reviewed by KaiXuan666 at 2019-10-10 04:09
  • 10. TickerProviderStateMixin dispose 报错

    在Lifecycle.dispose 中将正在执行的animationController dispose掉,会报错 image 感觉TickerProviderStateMixin 的dispose会先于ComponentState 中的dispose执行 `Effect buildEffect() { return combineEffects(<Object, Effect>{ Lifecycle.initState: _init, Lifecycle.dispose: _dispose, SeatsAction.volumeChanged: _onVolumeChanged, SeatsAction.selfVolumeChanged: _onSelfVolumeChanged, }); }

    void _init(Action action, Context ctx) { final Object tickerProvider = ctx.stfState; ctx.state.volumeAnimationControllers = List.generate(8, (index) { return AnimationController( duration: Duration(milliseconds: 1000), vsync: tickerProvider); }); }

    void _dispose(Action action, Context ctx) { ctx.state.volumeAnimationControllers.forEach((controller) { controller.dispose(); }); }`

    Reviewed by bitterking at 2019-09-11 09:32
  • 11. 能否对State中的final属性提供更好的支持

    如某个state

    class ExampleState implements Cloneable<ExampleState> {
      final bool editable;
      final Color color;
      final String id;
      String value;
      ExampleState({this.editable, this.color, this.id})
          : assert(editable != null),
            assert(color!= null),
            assert(id!= null);
      @override
      ExampleState clone() {
        return ExampleState (
          editable: editable,
          color: color,
          id: id,
        )
          ..value= value;
      }
    }
    

    对于上面这个State,要正常使用,必须在page的initstate或component的connector中做出特殊写法,例如:

    对于Page的initstate

    ExampleState Function(Map<String, dynamic>) initState(bool editable, Color color, String id) {
      return (parms) => ExampleState ( editable: editable,
          color: color,
          id: id,
      );
    }
    

    Page的构造函数:

    class ExamplePage extends Page<ExampleState, Map<String, dynamic>> {
    final bool editable;
    final Color color;
    final String id;
    ExamplePage({this.editable, this.color, this.id})
          : super(
              initState: initState(editable, color, id),
    ...// 后面的代码省略
    

    若这个State用于Component,则需要在Connector中进行类似的特殊处理,如:

    class ExampleConnector extends ConnOp<ParentState, ExampleState> {
      final bool editable;
      final Color color;
      final String id;
      ExampleConnector({this.editable, this.color, this.id});
      @override
      ExampleState get(ParentState state) {
        return ExampleState (
          editable: editable,
          color: color,
          id: id,
        )..value=state.ExampleValue;
      }
    
      @override
      void set(ParentState state, ExampleState subState) {}
    

    然后在使用此组件的page中:

    class ParentPage extends Page<ParentState, Map<String, dynamic>> {
      final bool editable;
      final String id;
      ParentPage ({this.editable = false, this.id})
          : super(
            // ... 省略代码
              dependencies: Dependencies<ParentState>(slots: <String, Dependent<ParentState>>{
                "exampleComponent": ExampleConnector(editable: editable, color: _colorsMap['ExampleTypeId'], id: 'ExampleTypeId') + ExampleComponent(),
              }),
             // ... 省略代码
            );
    }
    
    

    其实可以看得出来,State中的final属性应该来自于组件,如果能够让Component也具有initState方法,而且在initState函数中传入Component对象,就能够方面的为State中的final属性赋值了,也就不需要在initstate、组件构造函数、connector里面出现像变魔术一样变着花儿似的垃圾代码了。

    这是否有必要呢?

    Reviewed by johnhollon at 2019-04-01 00:46
  • 12. 好蛋疼啊,发邮件问又说更新维护,但是这么久了,就是不更新,如果停止维护了,请发个声明出来行吗,这么大个公司了

    如题,做了就好好的做,好好的更新了,如果不做直接说了,几次发邮件问你们,你们都说更新,但是这么久了啥也没有,如果不维护了,你们直接在官方发布声明暂停维护了就可以了 因为邮件问了你们,你们回复有说会持续的更新,直播中也问过你了,你们也说更新,这么久了,一直在等你们更新,如果不维护了直接说了,也不会在等了

    现在也不等了,发泄下,一个大公司做事情没这么干的,虽然很感谢你们的分享

    Reviewed by foolishlittlefox at 2022-03-25 02:24
  • 13. 关于InheritedWidget注册绑定不兼容问题

    fish_redux模板代码中的buildView方法在非pure情况下调用context.dependOnInheritedWidgetOfExactType方法无法进行绑定,该方法必须在widget的build方法中调用。 目前只能在buildView里面在新加一个无状态的widget来解决绑定问题,希望后续能够改进buildView在非pure情况的这个问题;

    Reviewed by JackGZY at 2022-02-24 09:24

Related

Built_redux provider for Flutter.

flutter_built_redux built_redux bindings for Flutter. By creating a Widget that extends StoreConnector you get automatic subscribing to your redux sto

Feb 14, 2022
Compile-time dependency injection for Dart and Flutter

package:inject Compile-time dependency injection for Dart and Flutter, similar to Dagger. NOTE: This is not an official Google or Dart team project. E

Aug 8, 2022
Github Search - Flutter Modular example
Github Search - Flutter Modular example

Desafio Github Search Projeto desenvolvido no desafio quizenal realizado pela equipe Flutterando e comunidade. Foram realizadas adaptações para implem

Nov 6, 2021
Flying Fish is full-stack Dart framework - a semi-opinionated framework for building applications exclusively using Dart and Flutter

Flying Fish is full-stack Dart framework - a semi-opinionated framework for building applications exclusively using Dart and Flutter.

Jul 28, 2022
The ROHD Verification Framework is a hardware verification framework built upon ROHD for building testbenches.
The ROHD Verification Framework is a hardware verification framework built upon ROHD for building testbenches.

ROHD Verification Framework The ROHD Verification Framework (ROHD-VF) is a verification framework built upon the Rapid Open Hardware Development (ROHD

Aug 2, 2022
Project demonstrates building a simple chat application using Flutter framework and Firebase cloud
Project demonstrates building a simple chat application using Flutter framework and Firebase cloud

Flutter Chat on Firebase Project demonstrates building a simple chat application using Flutter framework and Firebase cloud. App does not poll for new

Feb 2, 2022
This is an android application using Flutter Framework

quickpic A new Flutter application. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if

Nov 8, 2021
Newsly is an application built using the flutter framework.
Newsly is an application built using the flutter framework.

Newsly is an application built using the flutter framework. In this application, it has a feature to read news and add your own articles to the newsly application. Newsly uses the MVVM (Model-View-Viewmodel) architecture.

Jul 4, 2022
This project is an application that stores cash flow data using the flutter framework

cash_flow_journal This project is an application that stores cash flow data using the flutter framework. Getting Started This project is a starting po

Feb 12, 2022
A API integrated 5 day weather forecast and prediction application created using flutter framework and Dart language.

A API integrated 5 day weather forecast and prediction application created using flutter framework and Dart language. This API used here is OPEN WEATHER API, which specializes in predicting the weather of any city in this world.

Dec 26, 2021
Flutter_news - News application developed for practice, learning and testing the potential of this powerful Framework Flutter
Flutter_news - News application developed for practice, learning and testing the potential of this powerful Framework Flutter

flutter_news News application developed for practice, learning and testing the potential of this powerful Google's UI toolkit. Resources Packages pub

Jul 26, 2022
🏃Lossy is a mobile application built using flutter framework and firebase for android.
🏃Lossy is a mobile application built using flutter framework and firebase for android.

Lossy The Weight and Fitness Tracker App. Lossy is a mobile application built using flutter framework and firebase for android. Track your daily weigh

Aug 6, 2022
This is an application that uses the Flutter framework, SQFLite as a database to record blood pressure, blood sugar, BMI, or create medication reminders in multi mobile platforms You can run this project on iOS, Android
This is an application that uses the Flutter framework, SQFLite as a database to record  blood pressure, blood sugar, BMI, or create medication reminders in multi mobile platforms You can run this project on iOS, Android

This is an application that uses the Flutter framework, SQFLite as a database to record blood pressure, blood sugar, BMI, or create medication reminders in multi mobile platforms You can run this project on iOS, Android

Jun 28, 2022
ChitChat - a mobile application built using flutter framework and firebase for android
ChitChat - a mobile application built using flutter framework and firebase for android

This is a flutter app made using dart programming language . It uses firebase api to store and fetch data . It also uses native devices features such as library and camera . You can signup or login through your mail and can chat easily with your colleagues .

May 13, 2022
A Catalog Application Developed Using Flutter Framework/SDK.
A Catalog Application Developed Using Flutter Framework/SDK.

Catalog_App A Catalog Application Developed Using Flutter Framework/SDK. Technologies Used Front-End : Flutter Custom Design (UI/UX) : VelocityX State

May 10, 2022
In this tutorial, we'll create a Quiz application using AQUEDUCT FRAMEWORK.

Quiz API Dart An application built with aqueduct. Topics Covered How to Setup Aqueduct ? How to write your first REST API ? How to make controllers ?

Mar 2, 2022
🧡 The neuralgic heart of the application, this module gathers all the functionalities of the framework.
🧡 The neuralgic heart of the application, this module gathers all the functionalities of the framework.

Mineral Mineral is a robust, powerful and scalable framework designed to let you develop discord bots with the Dart language. The Mineral framework cu

Jul 21, 2022
A flutter project with a description of the basic Flutter framework
A flutter project with a description of the basic Flutter framework

Pengenalan kepada Mobile Platform (Slide) Apa itu Flutter Framework Pemasangan persekitaran Flutter, Android SDK & VSCode IDE Widget Layout & UI Eleme

Jan 3, 2022
Design system flutter - A framework contains SBB (Swiss Federal Railways) UI elements for Flutter Apps

Design System Mobile for Flutter Design System Mobile in Flutter (yes, it could

Jun 30, 2022