All Projects → hemanthrajv → Fludex

hemanthrajv / Fludex

Licence: bsd-2-clause
Flutter + Redux = Fludex

Programming Languages

dart
5743 projects

Projects that are alternatives of or similar to Fludex

Flutter Development Roadmap
Flutter App Developer Roadmap - A complete roadmap to learn Flutter App Development. I tried to learn flutter using this roadmap. If you want to add something please contribute to the project. Happy Learning
Stars: ✭ 474 (+2688.24%)
Mutual labels:  flutter-plugin
Flutter downloader
Flutter Downloader - A plugin for creating and managing download tasks. Supports iOS and Android. Maintainer: @hnvn
Stars: ✭ 546 (+3111.76%)
Mutual labels:  flutter-plugin
Flutterlocation
A Flutter plugin to easily handle realtime location in iOS and Android. Provides settings for optimizing performance or battery.
Stars: ✭ 711 (+4082.35%)
Mutual labels:  flutter-plugin
Responsiveframework
Easily make Flutter apps responsive. Automatically adapt UI to different screen sizes. Responsiveness made simple. Demo: https://gallery.codelessly.com/flutterwebsites/minimal/
Stars: ✭ 476 (+2700%)
Mutual labels:  flutter-plugin
Catcher
Flutter error catching & handling plugin. Handles and reports exceptions in your app!
Stars: ✭ 505 (+2870.59%)
Mutual labels:  flutter-plugin
Flutter candies
custom flutter candies(widgets) for you to build flutter app easily, enjoy it
Stars: ✭ 638 (+3652.94%)
Mutual labels:  flutter-plugin
Audioplayer
A flutter plugin to play audio files iOS / Android / MacOS / Web ( Swift/Java )
Stars: ✭ 461 (+2611.76%)
Mutual labels:  flutter-plugin
Flutter Geolocator
Android and iOS Geolocation plugin for Flutter
Stars: ✭ 759 (+4364.71%)
Mutual labels:  flutter-plugin
Flutter Learning
🔥 👍 🌟 ⭐ ⭐⭐ Flutter all you want.Flutter install,flutter samples,Flutter projects,Flutter plugin,Flutter problems,Dart codes,etc.Flutter安装和配置,Flutter开发遇到的难题,Flutter示例代码和模板,Flutter项目实战,Dart语言学习示例代码。
Stars: ✭ 4,941 (+28964.71%)
Mutual labels:  flutter-plugin
Liquid swipe flutter
A flutter based liquid swipe
Stars: ✭ 680 (+3900%)
Mutual labels:  flutter-plugin
Flutter Ffmpeg
FFmpeg plugin for Flutter
Stars: ✭ 494 (+2805.88%)
Mutual labels:  flutter-plugin
Flutter showcaseview
Flutter plugin that allows you to showcase your features on iOS and Android. 👌🔝🎉
Stars: ✭ 502 (+2852.94%)
Mutual labels:  flutter-plugin
Bottom navy bar
A beautiful and animated bottom navigation
Stars: ✭ 653 (+3741.18%)
Mutual labels:  flutter-plugin
Amap map fluttify
高德地图 地图组件 Flutter插件
Stars: ✭ 479 (+2717.65%)
Mutual labels:  flutter-plugin
Flutter thrio
flutter_thrio makes it easy and fast to add flutter to existing mobile applications, and provide a simple and consistent navigator APIs.
Stars: ✭ 717 (+4117.65%)
Mutual labels:  flutter-plugin
Audio service
Flutter plugin to play audio in the background while the screen is off.
Stars: ✭ 471 (+2670.59%)
Mutual labels:  flutter-plugin
Flutter secure storage
A Flutter plugin to store data in secure storage
Stars: ✭ 587 (+3352.94%)
Mutual labels:  flutter-plugin
Multi image picker
Flutter plugin that allows you to display multi image picker on iOS and Android. 👌🔝🎉
Stars: ✭ 889 (+5129.41%)
Mutual labels:  flutter-plugin
Flutter image cropper
A Flutter plugin for Android and iOS supports cropping images
Stars: ✭ 723 (+4152.94%)
Mutual labels:  flutter-plugin
Flutter Examples
An ultimate cheatbook of curated designs
Stars: ✭ 675 (+3870.59%)
Mutual labels:  flutter-plugin

Fludex 🔥

Flutter + Redux = Fludex

A redux based state managment library specialy build only for Flutter.

Why Fludex?

  • It is specialy built for Flutter and only works with Flutter.
  • It makes it easy to connect to store
  • It has built-in logger, thunk and futureMiddlewares
  • It uses simple wrapper to rebuild UI based on store

Basics

FludexState:

The application state is always of type FludexState. FludexState is implemented to make the state typesafe.

example:

FludexState<int> initState = new FludexState<int>(0);

Actions:

Fludex has a Action type. Any action dispatched to the store should be of type Action. The Action take two arguments type and payload (optional). The type defines what is the Type of Action and payload is some additional data dispatched to store.

example:

// A normal string action
Action stringAction = new Action(type: "SOME_ACTION",payload:"SOME_PAYLOAD");


// A [FutureAction] that can only be handled by [futureMiddleware].
Future<String> future = new Future<String>.delayed(
                          new Duration(seconds: 5),
                          () => "FutureAction Resolved");

Action futureAction = new Action(type: new FutureAction<String>(future));


// A [Thunk] action that is handled by [thunk] middleware.
Thunk thunkAction = (Store store) async {
    final int value =
        await new Future<int>.delayed(new Duration(seconds: 3), () => 0);
    store.dispatch(new Action(type: "UPDATE", payload: value));
  };

Reducers:

There is a Reducer type that takes initState (optional) and a StateReducer function as argument.

A StateReducer is a normal function that gets state and action as arguments, alters the state based on action and returns the new state.

// State
FludexState<int> initState = new FludexState<int>(0);

// Reducer function of type StateReducer
FludexState reducerFunction(FludexState fludexState, Action action){
    int state = fludexState.state;
	if(action.type == "INC")
    	state++;
    return new FludexState(state);
}

// Reducer
Reducer reducer = new Reducer(initState:initState,reduce:reducerFunction);

You can combine multiple reducers with built-in CombineReducer.

CombineReducer takes a Map<String,Reducer> as input. When using CombineReducer the state will be Map<String,dynamic> and the keys of reducer's map is used to build the state.

// Reducer-1
Reducer reducer1 = new Reducer(initState:initState1,reduce:reducerFunction1);

// Reducer-2
Reducer reducer2 = new Reducer(initState:initState2,reduce:reducerFunction2);

// Root Reducer
Reducer rootReducer = new CombineReducer({
                         "Screen1": reducer1
                         "Screen2": reducer2
                        });

// if initState1 = 0 and initState2 = 0, after applying CombineReducer the state will be { "Screen1":0, "Screen2":0 }

Store:

A fludex store has the following responsibilites.

  • Ensure only one instance of the store exists all over the application
  • Holds application state.
  • Allows access to state.
  • Allows to dispatch actions.
  • Registers listeners via [subscribe]

You should create a store before calling runApp.

Store take a single argument of type Map<String,dynamic> in which we specify the reducers and middlewares.

example:

// StateReducer
  final Reducer reducer = new CombinedReducer(
    {
      HomeScreen.name: HomeScreen.reducer,
      SecondScreen.name: SecondScreen.reducer
    }
  );

// Store Params
final Map<String, dynamic> params = <String, dynamic>{"reducer":reducer, "middleware": <Middleware>[logger, thunk, futureMiddleware]};

// Create the Store with params for first time.
final Store store = new Store(params);

// Run app
runApp(new MaterialApp(
          home: new HomeScreen(),
          routes: <String, WidgetBuilder>{
          HomeScreen.name: (BuildContext context) => new HomeScreen(),
          SecondScreen.name: (BuildContext context) => new SecondScreen()
       },));

Once Store created, one can easily connect to the store with StoreWrapper. StoreWrapper takes a builder function which is normal function that returns a Widget connected to some state via Store. Once store created you can get the state by creating new instance with null as params.

// If initState of Home is String initState = "Hello World!"
new StoreWrapper(
	builder: () => new Text(new Store(null).state["Home"]);
);

Middleware:

Middleware is somecode put between the dispatched action and the reducer receiving the action. Middleware is a normal function type that receives Store,Action and NextDispatcher as arguments. You can build your own middleware.

example:

// Example logger middleware
Middleware logger = (Store store, Action action, NextDispatcher next) {
       print('${new DateTime.now()}: $action');
       next(action);
     }

Built-in Middlewares:
logger:

A built-in logger middleware logs the Action, PreviousState, NextState and TimeStamp when applied.

example logs:

I/flutter ( 3949): [INFO] Fludex Logger: {
I/flutter ( 3949):   Action: FUTURE_DISPATCHED,
I/flutter ( 3949):   Previous State: {HomeScreen: 0, SecondScreen: {state: Begin, count: 0, status: FutureAction yet to be dispatched, loading: false}},
I/flutter ( 3949):   Next State: {HomeScreen: 0, SecondScreen: {state: Begin, count: 0, status: FutureAction Dispatched, loading: true}},
I/flutter ( 3949):   Timestamp: 2017-11-09 14:33:58.935510
I/flutter ( 3949): }
I/flutter ( 3949): [INFO] Fludex Logger: {
I/flutter ( 3949):   Action: FutureFulfilledAction{result: FutureAction Resolved},
I/flutter ( 3949):   Previous State: {HomeScreen: 0, SecondScreen: {state: Begin, count: 0, status: FutureAction Dispatched, loading: true}},
I/flutter ( 3949):   Next State: {HomeScreen: 0, SecondScreen: {state: Begin, count: 0, status: FutureAction Resolved, loading: false}},
I/flutter ( 3949):   Timestamp: 2017-11-09 14:34:03.919460
I/flutter ( 3949): }
thunk:

Built-in thunk middleware that is capable of handling Thunk actions. A Thunk action is just a function that takes Store as argument.

example:

// Example Thunk action
Thunk action = (Store store) async {
       final result = await new Future.delayed(
           new Duration(seconds: 3),
           () => "Result",
        );
       store.dispatch(result);
     };
futureMiddleware:

A built-in futureMiddleware that handles dispatching results of [Future] to the [Store]

The Future or FutureAction will be intercepted by the middleware. If the future completes successfully, a FutureFulfilledAction will be dispatched with the result of the future. If the future fails, a FutureRejectedAction will be dispatched containing the error that was returned.

example:

// First, create a reducer that knows how to handle the FutureActions:
// `FutureFulfilledAction` and `FutureRejectedAction`.
FludexState exampleReducer(FludexState fludexState,Action action) {
  String state = fludexState.state;
  if (action is String) {
  return action;
  } else if (action is FutureFulfilledAction) {
  return action.result;
  } else if (action is FutureRejectedAction) {
  return action.error.toString();
  }

 return new FludexState<String>(state);
}

// Next, create a Store that includes `futureMiddleware`. It will
// intercept all `Future`s or `FutureAction`s that are dispatched.
final store = new Store(
 {
   "reducer": exampleReducer,
   "middleware": [futureMiddleware],
 }
);

// In this example, once the Future completes, a `FutureFulfilledAction`
// will be dispatched with "Hi" as the result. The `exampleReducer` will
// take the result of this action and update the state of the Store!
store.dispatch(new Future(() => "Hi"));

// In this example, the initialAction String "Fetching" will be
// immediately dispatched. After the future completes, the
// "Search Results" will be dispatched.
store.dispatch(new FutureAction(
                 new Future(() => "Search Results"),
                 initialAction: "Fetching"
              ));

// In this example, the future will complete with an error. When that
// happens, a `FutureRejectedAction` will be dispatched to your store,
// and the state will be updated by the `exampleReducer`.
store.dispatch(new Future.error("Oh no!"));

Example

main function

void main(){

  // StateReducer
  final Reducer reducer = new CombinedReducer(
    {
      HomeScreen.name: HomeScreen.reducer,
      SecondScreen.name: SecondScreen.reducer
    }
  );

  // Store Params
  final Map<String, dynamic> params = <String, dynamic>{"reducer":reducer, "middleware": <Middleware>[logger, thunk, futureMiddleware]};

  // Create the Store with params for first time.
  final Store store = new Store(params);

  // Run app
  runApp(new MaterialApp(
    home: new HomeScreen(),
    routes: <String, WidgetBuilder>{
      HomeScreen.name: (BuildContext context) => new HomeScreen(),
      SecondScreen.name: (BuildContext context) => new SecondScreen()
    },
  ));
}

HomeScreen

class HomeScreen extends StatelessWidget {
  //Identifier for HomeScreen
  static final String name = "HomeScreen";

  // Reducer for HomeScreen
  static final Reducer reducer =
      new Reducer(initState: initState, reduce: _reducer);

  // Initial State of HomeScreen
  static final FludexState<int> initState = new FludexState<int>(0);

  // StateReducer function for HomeScreen
  static FludexState _reducer(FludexState fludexState, Action action) {
    int state_ = fludexState.state;
    if (action.type == "INC") state_++;
    if (action.type == "DEC") state_--;
    if (action.type == "UPDATE") state_ = action.payload;
    return new FludexState<int>(state_);
  }

  // Dispatches a "INC" action
  void _incrementCounter() {
    new Store(null).dispatch(new Action<String, Object>(type: "INC"));
  }

  // Dispatches a "DEC" action
  void _decrementCounter() {
    new Store(null).dispatch(new Action<String, Object>(type: "DEC"));
  }

  // A Thunk action that resets the state to 0 after 3 seconds
  static Thunk thunkAction = (Store store) async {
    final int value =
        await new Future<int>.delayed(new Duration(seconds: 3), () => 0);
    store.dispatch(new Action(type: "UPDATE", payload: value));
  };

  // Dispatches a thunkAction
  void _thunkAction() {
    new Store(null).dispatch(new Action(type: thunkAction));
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: const Text("HomeScreen"),
      ),
      body: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new StoreWrapper(
              builder: () =>
                  new Text(new Store(null).state[HomeScreen.name].toString())),
          new Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              new IconButton(
                  icon: new Icon(Icons.arrow_back),
                  onPressed: _decrementCounter),
              new IconButton(
                  icon: new Icon(Icons.arrow_forward),
                  onPressed: _incrementCounter)
            ],
          ),
          const Text(
            "Dispatch a Thunk Action which resolves a future and resets the store once future resolved",
            textAlign: TextAlign.center,
          ),
          new FlatButton(
              onPressed: _thunkAction,
              child: const Text("Dispatch Thunk Action"))
        ],
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: () => Navigator.of(context).pushNamed(SecondScreen.name),
        tooltip: 'Go to SecondScreen',
        child: new Icon(Icons.arrow_forward),
      ),
    );
  }
}

SecondScreen

class SecondScreen extends StatelessWidget {
  // Identifier for SecondScreen
  static final String name = "SecondScreen";

  // Reducer for SecondScreen
  static final Reducer reducer =
      new Reducer(initState: initState, reduce: _reducer);

  // Initial state of the screen
  static final FludexState<Map<String, dynamic>> initState = new FludexState<Map<String,dynamic>>(<String, dynamic>{
    "state": "Begin",
    "count": 0,
    "status": "FutureAction yet to be dispatched",
    "loading": false
  });

  // StateReducer function that mutates the state of the screen.
  // Reducers are just functions that knows how to handle state changes and retuns the changed state.
  static FludexState _reducer(FludexState _state, Action action) {
    Map<String, dynamic> state = _state.state;
    if (action.type == "CHANGE") {
      state["state"] = "Refreshed";
      state["count"]++;
    } else if (action.type is FutureFulfilledAction) {
      state["loading"] = false;
      state["status"] = action.type
          .result; // Result is be the value returned when a future resolves
      Navigator.of(action.payload["context"]).pop();
    } else if (action.type is FutureRejectedAction) {
      state["loading"] = false;
      state["status"] =
          action.type.error; // Error is the reason the future failed
      Navigator.of(action.payload["context"]).pop();
    } else if (action.type == "FUTURE_DISPATCHED") {
      state["status"] = action.payload["result"];
      state["loading"] = true;
      _onLoading(action.payload["context"]);
    }

    return new FludexState<Map<String,dynamic>>(state);
  }

  static void _onLoading(BuildContext context) {
    showDialog<dynamic>(
        context: context,
        barrierDismissible: false,
        child: new Container(
          padding: const EdgeInsets.all(10.0),
          child: new Dialog(
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                const CircularProgressIndicator(),
                const Text("Loading"),
              ],
            ),
          ),
        ));
  }

  // Dispatches a simple action with no Payload
  void _change() {
    new Store(null).dispatch(new Action<String, Object>(type: "CHANGE"));
  }

  // Builds and dispatches a FutureAction
  void _delayedAction(BuildContext context) {

    // A dummyFuture that resolves after 5 seconds
    final Future<String> dummyFuture = new Future<String>.delayed(
        new Duration(seconds: 5), () => "FutureAction Resolved");

    // An Action of type [FutureAction] that takes a Future to be resolved and a initialAction which is dispatched immedietly.
    final Action asyncAction = new Action(
        type: new FutureAction<String>(dummyFuture,
            initialAction: new Action(type: "FUTURE_DISPATCHED", payload: {
              "result": "FutureAction Dispatched",
              "context": context
            })),
        payload: {"context": context});

    // Dispatching a FutureAction
    new Store(null).dispatch(asyncAction);
  }

  // Builds a Text widget based on state
  Widget _buildText1() {

    final Map<String, dynamic> state = new Store(null).state[SecondScreen.name];
    final String value = state["state"] + " " + state["count"].toString();

    return new Container(
      padding: const EdgeInsets.all(20.0),
      child: new Text(value),
    );
  }

  // Builds a Text widget based on state
  Widget _buildText2() {

    final bool loading = new Store(null).state[SecondScreen.name]["loading"];

    return new Center(
      child: new Text(
        "Status: " + new Store(null).state[SecondScreen.name]["status"],
        style:
        new TextStyle(color: loading ? Colors.red : Colors.green),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: const Text("SecondScreen"),
      ),
      body: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new StoreWrapper(builder: _buildText1),
          const Text(
            "Dispatch a FutureAction that resolves after 5 seconds",
            textAlign: TextAlign.center,
          ),
          new StoreWrapper(builder: _buildText2),
          new FlatButton(
              onPressed: () => _delayedAction(context),
              child: const Text("Dispatch a future action"))
        ],
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _change,
        tooltip: 'Refresh',
        child: new Icon(Icons.refresh),
      ),
    );
  }
}

To run the example.

Author:

Hemanth Raj

LinkedIn

Built With :

Flutter - A framework for building crossplatform mobile applications.

References : redux.dart, redux-logger, redux-thunk & redux-future
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].