Implementation of the Flux framework for Flutter

Overview

flutter_flux

A Dart app architecture library with uni-directional data flow inspired by RefluxJS and Facebook's Flux.

This is an experimental package and does not have official support from the Flutter team. However, feedback is most welcome!


Overview

flux-diagram

flutter_flux implements a uni-directional data flow pattern comprised of Actions, Stores, and StoreWatchers. It is based on w_flux, but modified to use Flutter instead of React.

  • Actions initiate mutation of app data that resides in Stores.
  • Data mutations within Stores trigger re-rendering of app view (defined in StoreWatcher).
  • Flutter Widgets and other interaction sources dispatch Actions in response to user interaction.
  • and the cycle continues...

What's Included

Action

An Action is a command that can be dispatched (with an optional data payload) and listened to.

In flutter_flux, Actions are the sole driver of application state change. Widgets and other objects dispatch Actions in response to user interaction with the rendered view. Stores listen for these Action dispatches and mutate their internal data in response, taking the Action payload into account as appropriate.

import 'package:flutter_flux/flutter_flux.dart';

// define an action
final Action<String> displayString = new Action<String>();

// dispatch the action with a payload
displayString('somePayload');

// listen for action dispatches
displayString.listen(_displayAlert);

_displayAlert(String payload) {
  print(payload);
}

BONUS: Actions are await-able!

They return a Future that completes after all registered Action listeners complete. It's NOT generally recommended to use this feature within normal app code, but it is quite useful in unit test code.

Store

A Store is a repository and manager of app state. The base Store class provided by flutter_flux should be extended to fit the needs of your app and its data. App state may be spread across many independent stores depending on the complexity of the app and your desired app architecture.

By convention, a Store's internal data cannot be mutated directly. Instead, Store data is mutated internally in response to Action dispatches. Stores should otherwise be considered read-only, publicly exposing relevant data ONLY via getter methods. This limited data access ensures that the integrity of the uni-directional data flow is maintained.

A Store can be listened to to receive external notification of its data mutations. Whenever the data within a Store is mutated, the trigger method is used to notify any registered listeners that updated data is available. In flutter_flux, StoreWatchers listen to Stores, typically triggering re-rendering of UI elements based on the updated Store data.

import 'package:flutter_flux/flutter_flux.dart';

class RandomColorStore extends Store {

  // Public data is only available via getter method
  String _backgroundColor = 'gray';
  String get backgroundColor => _backgroundColor;

  // Actions relevant to the store are passed in during instantiation
  RandomColorActions _actions;

  RandomColorStore(RandomColorActions this._actions) {
    // listen for relevant action dispatches
    _actions.changeBackgroundColor.listen(_changeBackgroundColor);
  }

  _changeBackgroundColor(_) {
    // action dispatches trigger internal data mutations
    _backgroundColor = '#' + (new Random().nextDouble() * 16777215).floor().toRadixString(16);

    // trigger to notify external listeners that new data is available
    trigger();
  }
}

BONUS: Stores provide an optional terse syntax for action -> data mutation -> trigger operations.

// verbose syntax
actions.incrementCounter.listen(_handleAction);

_handleAction(payload) {
    // perform data mutation
    counter += payload;
    trigger();
  }

// equivalent terse syntax
triggerOnAction(actions.incrementCounter, (payload) => counter += payload);

Examples

Simple examples of flutter_flux usage can be found in the example directory. The example README includes instructions for building / running them.


External Consumption

flutter_flux implements a uni-directional data flow within an isolated application or code module. If flutter_flux is used as the internal architecture of a library, this internal data flow should be considered when defining the external API.

  • External API methods intended to mutate internal state should dispatch Actions, just like any internal user interaction.
  • External API methods intended to query internal state should leverage the existing read-only Store getter methods.
  • External API streams intended to notify the consumer about internal state changes should be dispatched from the internal Stores, similar to their triggers.
Comments
  • Pubspec.yaml is missing for chat_app

    Pubspec.yaml is missing for chat_app

    Could you upgrade the instructions as i want to play around with this example...

    env: x-MacBook-Pro:chat_app apple$ flutter doctor [✓] Flutter (on Mac OS, channel master) • Flutter at /usr/local/flutter/flutter • Framework revision 16ce090fde (5 hours ago), 2016-11-01 20:34:36 • Engine revision 63e71803de • Tools Dart version 1.21.0-dev.2.0

    [✓] Android toolchain - develop for Android devices (Android SDK 24.0.3) • Android SDK at /Users/apple/Library/Android/sdk • Platform android-24, build-tools 24.0.3 • ANDROID_HOME = /Users/apple/Library/Android/sdk • Java(TM) SE Runtime Environment (build 1.8.0_60-b27)

    [✓] iOS toolchain - develop for iOS devices (Xcode 8.1) • XCode at /Applications/Xcode.app/Contents/Developer • Xcode 8.1, Build version 8B62

    [x] Flutter IDE Support (No supported IDEs installed) • IntelliJ - https://www.jetbrains.com/idea/

    [✓] Connected devices • None x-MacBook-Pro:chat_app apple$

    error:

    x-MacBook-Pro:chat_app apple$ pwd
    /Users/apple/workspace/go/src/github.com/flutter/flutter_flux/example/chat_app
    x-MacBook-Pro:chat_app apple$ pub get
    Could not find a file named "pubspec.yaml" in "/Users/apple/workspace/go/src/github.com/flutter/flutter_flux/example/chat_app".
    x-MacBook-Pro:chat_app apple$ 
    
    
    bug 
    opened by joeblew99 7
  • StoreWatcherMixin can't be used as mixin because it extends a class other than Object

    StoreWatcherMixin can't be used as mixin because it extends a class other than Object

    IDE: VSCode

    Doesn't seem to preclude builds and debugging, however is triggering a heap of 'errors' in analyzer.

    Thoughts?

    `[✓] Flutter (Channel dev, v0.10.1, on Mac OS X 10.13.6 17G65, locale en-NZ) • Flutter version 0.10.1 at /Users/greg/Library/Developer/flutter • Framework revision 6a3ff018b1 (13 days ago), 2018-10-18 18:38:26 -0400 • Engine revision 3860a43379 • Dart version 2.1.0-dev.7.1.flutter-b99bcfd309

    [✓] Android toolchain - develop for Android devices (Android SDK 28.0.3) • Android SDK at /Users/greg/Library/Android/sdk • Android NDK at /Users/greg/Library/Android/sdk/ndk-bundle • Platform android-28, build-tools 28.0.3 • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06) • All Android licenses accepted.

    [✓] iOS toolchain - develop for iOS devices (Xcode 10.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Xcode 10.1, Build version 10B61 • ios-deploy 1.9.2 • CocoaPods version 1.5.3

    [✓] Android Studio (version 3.2) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin version 29.1.1 • Dart plugin version 181.5656 • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06) [✓] VS Code (version 1.28.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 2.19.0

    [!] Connected device ! No devices available! Doctor found issues in 1 category.greg@iMac:~/Projects/Tierra/logmate_flutter$`

    screen shot 2018-11-01 at 17 41 34

    opened by gmcdowell 5
  • triggerOnConditionalAction parameter type error

    triggerOnConditionalAction parameter type error

    in store.dart,

    class Store {
        // ...
    void triggerOnConditionalAction<T>(Action<T> action, bool onAction(T payload)) {
        assert(action != null);
        action.listen((dynamic payload) async {
          // Action functions must return bool, or a Future<bool>.
          dynamic result = onAction(payload);
          bool wasChanged;
          if (result is Future) {
            wasChanged = await result;
          } else {
            wasChanged = result;
          }
          if (wasChanged) {
            trigger();
          }
        });
      }
        // ...
    }
    

    the 2nd param onAction defined bool return type. it should be dynamic.

    opened by Amot-zpan 4
  • How to change TextField text from Store?

    How to change TextField text from Store?

    In the example, when we commit a new message, the _currentMessage is emptied but the TextField does not reflect that changes.

    This is the code in the store:

    triggerOnAction(commitCurrentMessageAction, (ChatUser me) {
          final ChatMessage message =
              new ChatMessage(sender: me, text: _currentMessage);
          _messages.add(message);
          _currentMessage = '';
        });
    

    The view uses a TextEditingController as a controller for the TextField Widget so I understand why it is not updated.

    How can we empty the TextField from the Store with flutter_flux?

    opened by letsar 4
  • Chat example should be updated to current Flutter version

    Chat example should be updated to current Flutter version

    Which means moving to new Gradle build as well as some changes in the Flutter API itself (ListTile instead of ListItem, ListView instead of Block, etc.).

    I've already done so locally and got the app running with today's Flutter version. I can also submit a pull request, but should I put the updated example in a new folder or just overwrite example/chap_app?

    opened by DavidMihola 4
  • Remove unnecessary step to run the app

    Remove unnecessary step to run the app

    Apparently "flutter run" includes the "pub get" step.

    chat_app [master]$ flutter run Running 'flutter packages get' in chat_app... 2419ms New users installing Flutter won't necessarily know about the "pub" tool, so removing it will reduce friction for those users.

    opened by franklinharper 4
  • Clean up flux interfaces

    Clean up flux interfaces

    • Rather than using complex mixin inheritance, we now use composition for StoreListener.
    • Rename StoreWatcher.initState to StoreWatcher.initStores to suggest that this function is for initializing the stores rather than initializing the (hidden) state object.
    • Update the dartdocs to reflect how the code works currently.
    opened by abarth 4
  • Is it okay to update widget's store in action callback?

    Is it okay to update widget's store in action callback?

    I am referring to the example provided in this repo. https://github.com/google/flutter_flux/blob/master/example/lib/main.dart#L62

    The handleChatMessageStoreChanged function is called with the Store parameter and from the example it looks like, the downcasted store is assigned to a local scoped store and not the widget's store directly.

    Can that function be written as

    class ChatScreenState extends State<ChatScreen>
        with StoreWatcherMixin<ChatScreen> {
      // Never write to these stores directly. Use Actions.
      ChatMessageStore messageStore;
      // ...
      void handleChatMessageStoreChanged(Store store) {
        // ChatMessageStore messageStore = store; // instead of this local scoped store ...
        messageStore = store; // ... assign to widget's store
        if (messageStore.currentMessage.isEmpty) {
            msgController.clear();
        }
        setState(() {});
      }
      // ...
    }
    

    If this is not okay, I would like to know why.

    opened by hvkale 3
  • Switch to using SDK source

    Switch to using SDK source

    After this patch, we now depend on package:flutter using an SDK source, which means package:flutter doesn't need to be at a particular path relative to this package anymore.

    opened by abarth 3
  • Outdated SDK version

    Outdated SDK version

    I'm getting the following error:

    Because cronicalia_flutter depends on flutter_flux >=4.0.1 which requires SDK version >=1.19.0 <2.0.0, version solving failed.

    Could you update the pubspec.yaml? Instead of

    environment:
      sdk: ">=1.24.0 <2.0.0"
    

    use

    environment:
      sdk: ">=1.24.0 <3.0.0"
    
    opened by AndreHaueisen 2
  • StoreWatcherMixin Widget not rebuilding

    StoreWatcherMixin Widget not rebuilding

    Hi,

    I've been wrestling with this for a while now and can't see what is going wrong, but my StoreWatcherMixin widget does not get build() called after setting state in the flux store.

    Main.dart:

    void main() => runApp(new MyApp());
    
    class MyApp extends StatefulWidget {
      @override
      State createState() => new AppState();
    }
    
    class AppState extends State<MyApp>
      with StoreWatcherMixin<MyApp> {
    
      var appStore;
    
      @override
      void initState() {
        super.initState();
        appStore = listenToStore(_appStoreToken);
      }
    
      @override
      Widget build(BuildContext context) {
          ...
          return new SecondPage(_appStoreToken);
       }
    

    The store:

    final Action<List<Foo>> setFoos = new Action<List<Foo>>();
    final AppStore _store = new AppStore();
    final StoreToken _appStoreToken = new StoreToken(_store);
    
    class AppStore extends Store {
      List<Foo> _allFoos = new List<Foo>();
      List<ArkhamDBCardPack> get allFoos => _allFoos;
    
      AppStore() {
        triggerOnAction(setFoos, (value) => _allFoos = value);
      }
    }
    

    Second page:

    class SecondPage extends StatefulWidget {
    
      final StoreToken _storeToken;
      SecondPage(this._storeToken);
    
      @override
      State createState() => new SecondPageState(_storeToken);
    }
    
    class SecondPageState extends State<SecondPage>
      with StoreWatcherMixin<SecondPage> {
    
      var appStore;
      final StoreToken _storeToken;
      List<Foo> foos;
    
      SecondPageState(this._storeToken);
    
      @override
      void initState() {
        super.initState();
        debugPrint('x');
        appStore = listenToStore(_storeToken);
        foos = appStore.allFoos;
      }
    
      @override
      Widget build(BuildContext context) {
        debugPrint('rebuilding');
        return new IconButton(
                  icon: new Icon(Icons.refresh),
                  onPressed: () async {
                    var data = await fetchFoos();
                    setFoos.call(data);
                  },
                )
       }
    }
    

    This isn't the exact code, but its close enough. The first time it runs, I press the button on the second page to fetch the data and call setFoos. debugPrints show that it has set the value correctly into the store, but the build() and debugPrint('rebuilding') are never called a second time.

    What am I doing wrong? I really want to get this working as flutter and flux seem like a great tech. Thanks in advance!

    opened by JamesWatton 2
  • Method listening to an action gets called multiple times

    Method listening to an action gets called multiple times

    If a method is listening to an action then for first time it gets hit once, then for next time if user gets back to the same page then the method gets hit twice and on third time its 3 times and increases every time.

    Below is a code to simulate the same on click of Next a action is called and on action change the user is taken to second page, but on second time the action listening method gets hit twice.

        import 'package:flutter/material.dart';
        import 'package:flutter_flux/flutter_flux.dart';
    
        void main() => runApp(MyApp());
    
        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return MaterialApp(
              home: MyHome(),
            );
          }
        }
        //========
        class MyHome extends StatefulWidget {
          @override
          _MyHomeState createState() => _MyHomeState();
        }
    
        class _MyHomeState extends State<MyHome> with StoreWatcherMixin<MyHome>  {
          AppStore store;
          @override
          void initState() {
            store = listenToStore(appStoreToken);
            changeStatus.listen(_openSetupDialogue);
            super.initState();
          }
    
          void _openSetupDialogue(dynamic payload){
            if(payload == true) {
              print("method got hit");
              Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));
            }
          }
    
          @override
          Widget build(BuildContext context) {
            return RaisedButton(
              child: Text("Next"),
              onPressed: (){
                changeStatus(true);
              },
            );
          }
        }
        //===========
        class SecondPage extends StatefulWidget {
          @override
          _SecondPageState createState() => _SecondPageState();
        }
    
        class _SecondPageState extends State<SecondPage> {
          @override
          Widget build(BuildContext context) {
            return RaisedButton(
              child: Text("back"),
              onPressed: (){Navigator.push(context, MaterialPageRoute(builder: (context) =>MyHome()),
                );
              },
            );
          }
        }
        //===========
        class AppStore extends Store {
          bool state = true;
          AppStore(){
            triggerOnAction(changeStatus, (status){
              state = status;
            });
          }
          bool get getAppStore => state;
        }
        final Action changeStatus = Action<bool>();
        final appStoreToken = StoreToken(AppStore());
    
    opened by DhudumVishal 4
  • how to listen to similar stores with different arguments

    how to listen to similar stores with different arguments

    There are some tab pages like fragment on android, I use constructor arguments to conotrol their stores. Like this:

    List<GankPage> pages = [
      TabPage('aaa'),
      TabPage('bbb'),
    ]
    
    class TabPage extends StoreWatcher {
      String type;
      StoreToken pageStoreToken;
      TabPage({Key k, this.type}) : super(key: k);
    
      @override
      void initStores(ListenToStore listenToStore) {
        pageStoreToken = StoreToken(PageStore(this.type));
        listenToStore(pageStoreToken);
        pageAction.call();
      }
    
    }
    

    If I write like this, when I switch tab the old actions will be called. I saw the note is that stores should not depend on any constructor parameters. Any suggetions?

    opened by lzh77 1
  • How to make network calls?

    How to make network calls?

    The title of this issue could be a little misleading but I was not sure how else to describe my thought process, so please stay with me and read along.

    I am writing a Flutter application that uses flutter_flux. I have a few stores and a few views (widgets) so far. Right now I am in a dilemma on how to do the network API calls. My widget loads but does not have any data to display yet, which is fine, but now it needs to call my network API service that responds with data. Here is what I am thinking design-wise. Please share your opinions on it.

    Widget -> Action -> Store -> Network Service Call -> trigger()

    In this design, the widget kicks off an action something like fetchMessages and the store listening to this action MessageStore receives a call to this action. In the action callback, this store kicks the network call that returns Future. In the then() callback of that Future, this store mutates it's own data and calls trigger() so that the original view that started this now receives a callback. Similarly, also in catchError of the same Future, this store calls trigger() with some other mutation that maintains error states.

    In this design, I am not exactly sure where do I call action from the widget? In build or in initState? My thinking is that in initState the widget will grab data from store but it will be null or empty initially, and build will render empty view but at the same time, kickstarts the cycle described above, which calls back to the widget later and the build function now has data to render, re-grabbed from store.

    Do you think it is a good pattern? It still abides to Flux's unidirectional data flow and does not let store data to be manipulated in any way other than an action callback.

    opened by hvkale 1
Owner
Google
Google ❤️ Open Source
Google
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 Dec 31, 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 863 Dec 31, 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
Flux - E-commerce app with flutter

E-commerce app with flutter ?? you can also buy me coffee ?? Requirements ?? Any

Dami 27 Jul 13, 2022
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 Dec 27, 2022
Intel Corporation 238 Dec 24, 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 18 Dec 20, 2022
Get It - Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App. Maintainer: @escamoteur

❤️ Sponsor get_it This is a simple Service Locator for Dart and Flutter projects with some additional goodies highly inspired by Splat. It can be used

Flutter Community 1k Jan 1, 2023
Dart GraphQL server implementation. Utilities, code generator, examples and reference implementation.

Leto - GraphQL Server A complete implementation of the official GraphQL specification in the Dart programming language. Inspired by graphql-js, async-

Juan Manuel Castillo 29 Nov 27, 2022
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

Mohamad Zaki Mustafa 3 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

Swiss Federal Railways (SBB) 14 Dec 22, 2022
Fully functional Twitter clone built in flutter framework using Firebase realtime database and storage

Fwitter - Twitter clone in flutter A working Twitter clone written in Flutter using Firebase auth,realtime,firestore database and storage. Download Ap

Sonu Sharma 2.4k Jan 8, 2023
A minimalist Flutter framework for rapidly building custom designs.

Show some ❤️ and star the repo. VelocityX is a 100% free Flutter open-source minimalist UI Framework built with Flutter SDK to make Flutter developmen

Pawan Kumar 1.2k Jan 8, 2023
An assembled flutter application framework.

Fish Redux What is Fish Redux ? Fish Redux is an assembled flutter application framework based on Redux state management. It is suitable for building

Alibaba 7.3k Jan 7, 2023
Simple form maker for Flutter Framework

Flutter FormBuilder - flutter_form_builder This package helps in creation of data collection forms in Flutter by removing the boilerplate needed to bu

Danvick Miller 1.2k Jan 3, 2023
Auto is a Flutter automated testing framework developed for testers.

Auto Auto-A simpler Flutter UI automation test solution. No need to write any code Recording test scripts is very simple Mult

null 19 Oct 12, 2022
A simple and easy to learn declarative navigation framework for Flutter, based on Navigator 2.0.

A simple and easy to learn declarative navigation framework for Flutter, based on Navigator 2.0 (Router). If you love Flutter, you would love declarat

Zeno Nine 20 Jun 28, 2022
Quiz App to conduct online quiz developed with flutter framework and dart language

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

Parag Patil 7 Nov 8, 2022
Simple tool to open WhatsApp chat without saving the number, developed using Google's Flutter Framework. for Android/ IOS/ Desktop/ Web

OpenWp Simple tool to open WhatsApp chat without saving the number Explore the docs » View Demo · Report Bug · Request Feature Table of Contents About

Swarup Bhanja Chowdhury 15 Nov 1, 2022
An extensible flutter-framework

dart_board An extensible flutter-framework Dart Board allows you to break your app into features and then integration cleanly and consistently. It's e

Adam Hammer 56 Dec 19, 2022