An assembled flutter application framework.

Overview

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

Comments
  • 关于fish-redux支持 App级别Store的讨论

    关于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', {});
    

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

    the key issue 
    opened by zjuwjf 60
  • 收集大家对fish-redux后续演进的期望

    收集大家对fish-redux后续演进的期望

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

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

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

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

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

    opened by zjuwjf 51
  • action如何修改当前页面的reducer

    action如何修改当前页面的reducer

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

    opened by jefferybai 24
  • 配合 built value 创建 state 合理吗?

    配合 built value 创建 state 合理吗?

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

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

    opened by TshineZheng 20
  • 所有component都依赖的global state的reselect方法

    所有component都依赖的global state的reselect方法

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

    opened by ykrank 17
  • TabBarView中使用Adapter的若干问题

    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. 能正确保持页面状态,能正确切换
    opened by jing-pei 17
  • 能否在是用list adapter时,子component的action只发送到相应的子reducer?

    能否在是用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。

    good first issue 
    opened by danieldai 17
  • component 无法更新的问题

    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,我该如何获取到正确的数据呢

    opened by hasonguo 16
  • TodoEditPage中每次修改编辑框内容,都会重新buildView(),这样难道不会影响性能吗?

    TodoEditPage中每次修改编辑框内容,都会重新buildView(),这样难道不会影响性能吗?

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

    good first issue 
    opened by KaiXuan666 14
  • TickerProviderStateMixin dispose 报错

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

    the key issue 
    opened by bitterking 14
  • 能否对State中的final属性提供更好的支持

    能否对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里面出现像变魔术一样变着花儿似的垃圾代码了。

    这是否有必要呢?

    opened by johnhollon 14
  • 好蛋疼啊,发邮件问又说更新维护,但是这么久了,就是不更新,如果停止维护了,请发个声明出来行吗,这么大个公司了

    好蛋疼啊,发邮件问又说更新维护,但是这么久了,就是不更新,如果停止维护了,请发个声明出来行吗,这么大个公司了

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

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

    opened by foolishlittlefox 5
  • 关于InheritedWidget注册绑定不兼容问题

    关于InheritedWidget注册绑定不兼容问题

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

    opened by JackGZY 0
Owner
Alibaba
Alibaba Open Source
Alibaba
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

null 81 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

Google 861 Sep 13, 2022
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

Flutterando 24 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.

Flutter Fish 3 Jul 28, 2022
Intel Corporation 223 Sep 21, 2022
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

Intel Corporation 15 Aug 14, 2022
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

Sukitha Udugamasooriya 8 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

Havishwaran J 0 Nov 8, 2021
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.

Muhammad Zaim Maulana 12 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

Firman Ali 0 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.

Nitin Verma 0 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 Google's UI toolkit. Resources Packages pub

Rafael Almeida Barbosa 488 Sep 25, 2022
🏃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

LakhanKumawat ᵖ⁺ 6 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

null 10 Aug 26, 2022
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 .

LakhanKumawat ᵖ⁺ 4 May 13, 2022
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

Adhil 2 May 10, 2022
Bike rental mobile application made using Flutter Framework.

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

Youssef Toumi Benchekroun 2 Aug 13, 2022
Tasawq App — Flutter framework and Firebase An application that objectives to display all nearby stores of all kinds and real estate.

Tasawq App — Flutter framework and Firebase An application that objectives to display all nearby stores of all kinds and real estate. Multi-vendor, standard user login to view nearby products and stores for rating, follow-up, messaging and more

null 2 Sep 11, 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 ?

Pawan Kumar 19 Mar 2, 2022
🧡 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

mineral.dart 11 Sep 20, 2022