• Stars
    star
    1,648
  • Rank 28,365 (Top 0.6 %)
  • Language
    Dart
  • License
    MIT License
  • Created almost 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library that connects Widgets to a Redux Store

flutter_redux

Build Status codecov

A set of utilities that allow you to easily consume a Redux Store to build Flutter Widgets.

This package supports null-safety and is built to work with Redux.dart 5.0.0+ and Flutter 3+.

Redux Widgets

  • StoreProvider - The base Widget. It will pass the given Redux Store to all descendants that request it.
  • StoreBuilder - A descendant Widget that gets the Store from a StoreProvider and passes it to a Widget builder function.
  • StoreConnector - A descendant Widget that gets the Store from the nearest StoreProvider ancestor, converts the Store into a ViewModel with the given converter function, and passes the ViewModel to a builder function. Any time the Store emits a change event, the Widget will automatically be rebuilt. No need to manage subscriptions!

Examples

  • Simple example - a port of the standard "Counter Button" example from Flutter
  • Github Search - an example of how to search as a user types, demonstrating both the Middleware and Epic approaches.
  • Todo app - a more complete example, with persistence, routing, and nested state.
  • Timy Messenger - large open source app that uses flutter_redux together with Firebase Firestore.

Companion Libraries

Usage

Let's demo the basic usage with the all-time favorite: A counter example!

Note: This example requires flutter_redux 0.4.0+ and Dart 2! If you're using Dart 1, see the old example.

import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';

// One simple action: Increment
enum Actions { Increment }

// The reducer, which takes the previous count and increments it in response
// to an Increment action.
int counterReducer(int state, dynamic action) {
  return action == Actions.Increment ? state + 1 : state;
}

void main() {
  // Create your store as a final variable in the main function or inside a
  // State object. This works better with Hot Reload than creating it directly
  // in the `build` function.
  final store = Store<int>(counterReducer, initialState: 0);

  runApp(FlutterReduxApp(
    title: 'Flutter Redux Demo',
    store: store,
  ));
}

class FlutterReduxApp extends StatelessWidget {
  final Store<int> store;
  final String title;

  FlutterReduxApp({Key key, this.store, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // The StoreProvider should wrap your MaterialApp or WidgetsApp. This will
    // ensure all routes have access to the store.
    return StoreProvider<int>(
      // Pass the store to the StoreProvider. Any ancestor `StoreConnector`
      // Widgets will find and use this value as the `Store`.
      store: store,
      child: MaterialApp(
        theme: ThemeData.dark(),
        title: title,
        home: Scaffold(
          appBar: AppBar(title: Text(title)),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                // Connect the Store to a Text Widget that renders the current
                // count.
                //
                // We'll wrap the Text Widget in a `StoreConnector` Widget. The
                // `StoreConnector` will find the `Store` from the nearest
                // `StoreProvider` ancestor, convert it into a String of the
                // latest count, and pass that String  to the `builder` function
                // as the `count`.
                //
                // Every time the button is tapped, an action is dispatched and
                // run through the reducer. After the reducer updates the state,
                // the Widget will be automatically rebuilt with the latest
                // count. No need to manually manage subscriptions or Streams!
                StoreConnector<int, String>(
                  converter: (store) => store.state.toString(),
                  builder: (context, count) {
                    return Text(
                      'The button has been pushed this many times: $count',
                      style: Theme.of(context).textTheme.display1,
                    );
                  },
                )
              ],
            ),
          ),
          // Connect the Store to a FloatingActionButton. In this case, we'll
          // use the Store to build a callback that will dispatch an Increment
          // Action.
          //
          // Then, we'll pass this callback to the button's `onPressed` handler.
          floatingActionButton: StoreConnector<int, VoidCallback>(
            converter: (store) {
              // Return a `VoidCallback`, which is a fancy name for a function
              // with no parameters and no return value. 
              // It only dispatches an Increment action.
              return () => store.dispatch(Actions.Increment);
            },
            builder: (context, callback) {
              return FloatingActionButton(
                // Attach the `callback` to the `onPressed` attribute
                onPressed: callback,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              );
            },
          ),
        ),
      ),
    );
  }
}

Purpose

One question that reasonable people might ask: Why do you need all of this if StatefulWidget exists?

My advice is the same as the original Redux.JS author: If you've got a simple app, use the most straightforward thing possible. In Flutter, StatefulWidget is perfect for a simple counter app.

However, say you have the more complex app, such as an E-commerce app with a Shopping Cart. The Shopping Cart should appear on multiple screens in your app and should be updated by many different types of Widgets on those different screens (An "Add Item to Cart" Widget on all your Product Screens, "Remove Item from Cart" Widget on the Shopping Cart Screen, "Change quantity" Widgets, etc.).

Additionally, you want to test this logic, as it's the core business logic to your app!

Now, in this case, you could create a Testable ShoppingCart class as a Singleton or Create a Root StatefulWidget that passes the ShoppingCart Down Down Down through your widget hierarchy to the "add to cart" or "remove from cart" Widgets.

Singletons can be problematic for testing, and Flutter doesn't have a great Dependency Injection library (such as Dagger2) just yet, so I'd prefer to avoid those.

Yet passing the ShoppingCart all over the place can get messy. It also means

it's way harder to move that "Add to Item" button to a new location b/c you'd need up update the Widgets throughout your app that passes the state down.

Furthermore, you'd need a way to Observe when the ShoppingCart Changes so you could rebuild your Widgets when it does (from an "Add" button to an "Added" button, as an example).

One way to handle it would be to simply setState every time the ShoppingCart changes in your Root Widget, but then your whole app below the RootWidget would be required to rebuild as well! Flutter is fast, but we should be thoughtful about what we ask Flutter to rebuild!

Therefore, redux & redux_flutter was born for more complex stories like this one. It gives you a set of tools that allow your Widgets to dispatch actions in a naive way, then writes the business logic in another place that will take those actions and update the ShoppingCart in a safe, testable way.

Even more, once the ShoppingCart has been updated in the Store, the Store will emit an onChange event. This lets you listen to Store updates and rebuild your UI in the right places when it changes! Now, you can separate your business logic from your UI logic in a testable, observable way, without having to Wire up a bunch of stuff yourself!

Similar patterns in Android are the MVP Pattern or using Rx Observables to manage a View's State.

flutter_redux handles passing your Store down to all of your descendant StoreConnector Widgets. If your State emits a change event, only the StoreConnector Widgets and their descendants will be automatically rebuilt with the latest State of the Store!

This allows you to focus on what your app should look like and how it should work without thinking about all the glue code to hook everything together!

Contributors

More Repositories

1

flutter_architecture_samples

TodoMVC for Flutter
Dart
8,720
star
2

scoped_model

A Widget that passes a Reactive Model to all of it's children
Dart
774
star
3

bansa

A state container for Java & Kotlin, inspired by Redux & Elm
Kotlin
442
star
4

flutter_stetho

Integrate Flutter with the Facebook Stetho tool for Android
Dart
219
star
5

dart_redux_epics

Redux.dart middleware for handling actions using Dart Streams
Dart
141
star
6

new_flutter_template

Test ideas for a new flutter template
Dart
140
star
7

transparent_image

A transparent image in Dart code, represented as a Uint8List.
Dart
103
star
8

redux_thunk

Redux Middleware for handling functions as actions
Dart
88
star
9

github_search_angular_flutter

Demonstrates how to use the bloc pattern for both an Angular and Flutter app
CSS
85
star
10

flutter_stream_friends

Flutter's great. Streams are great. Let's be friends.
Dart
62
star
11

gradient_animations

An example of how to animate gradients in flutter
Dart
61
star
12

flutter_redux_dev_tools

A Time Traveling Redux Debugger for Flutter
Makefile
40
star
13

reselect_dart

Efficiently derive data from your Redux Store
Dart
37
star
14

redux_logging

Redux.dart Middleware that prints the latest action & state
Dart
30
star
15

rainbow_gradient

An easy way to add Rainbows to your Flutter apps!
Dart
23
star
16

giphy_client

A Giphy API Client for Dart compatible with all platforms
Dart
22
star
17

mvi_sealed_unions

dart_sealed_union playground, started with code from Flutter Consortium
Dart
21
star
18

memory_image_converter

A command line app that will convert an image into a Uint8List which can be consumed by a Flutter app using the `MemoryImage`, `Image.memory`, or `FadeInImage.memoryNetwork` classes.
Dart
16
star
19

hacker_news_client

A client for the Hacker News API. Works on Flutter, Server, and Web.
Dart
16
star
20

hnpwa_client

Fetch data from the HNPWA API -- a Hacker News API crafted for mobile and progressive web apps
Dart
14
star
21

open_iconic_flutter

The Open Iconic icon pack available as a set of Flutter Icons
Dart
14
star
22

rxdart_codemod

A collection of codemods to upgrade your RxDart code from one version to the next
Dart
11
star
23

redux_future

A Redux Middleware for handling Dart Futures as actions
Dart
10
star
24

draggable_scrollbar

A scrollbar that can be used to quickly navigate through a list
Dart
10
star
25

fancy_network_image

A port of cached_network_image all the goodies EXCEPT the cache
Dart
10
star
26

scoped_model_github_search

Translated the RxDart Github Search example to ScopedModel
Dart
9
star
27

redux_dev_tools

Time Travel Dev Tools for Dart Redux
Dart
8
star
28

CSScaffold.tmbundle

Like or want to learn CSScaffold, but tired of referring to the documentation or looking through Mixins.css files? Use the the Textmate bundle!
6
star
29

flutter_lcov_docker

Flutter + Lcov for code Coverage reports!
5
star
30

swapi_client

A library that makes it easy to fetch data from SWAPI: The Star Wars API.
Dart
5
star
31

workshop_live_coding_login

Dart
4
star
32

nested_bottom_navigation

A test of how to do nested bottom navigation with BottomNavigation
Dart
4
star
33

flutter_ui_challenge_zoom_menu

Flutter UI Challenge: Zoom Menu.
Dart
3
star
34

dotfiles

The dotfiles of Brian Egan
HTML
3
star
35

stream_store

A Redux-style Store implemented using core Dart Stream primitives
Dart
3
star
36

hacker_news_app

Hacker news app for flutter
Dart
3
star
37

ez_listenable

A set utilities that allow you to listen to objects and notify listeners of changes
CSS
3
star
38

dribbble_client

A Dribbble api client for Dart
Dart
3
star
39

monocle

View CONTENTdm images with style!
JavaScript
2
star
40

Scaffold.ruble

Aptana Bundle for the Scaffold CSS preprocessor!
Ruby
2
star
41

Playduino

JavaScript
2
star
42

Vim-Configuration

My personal vim folder!
Vim Script
1
star
43

BangumiList

萌豚 η•ͺ剧清单
Dart
1
star
44

loadTest.js

A simple load test script for phantomjs
1
star
45

dmsuggest

A Search Suggestion App for dmBridge
JavaScript
1
star
46

HotTest

The hottest way to run client-side unit tests
JavaScript
1
star
47

pic_in_a_box

A swank replica of Sexy Slider.
JavaScript
1
star
48

Dilefont

Such a dilettante
JavaScript
1
star
49

transmit-vim

Integrates Transmit FTP (Mac) with Vim
Vim Script
1
star
50

dmcarousel

A slideshow application for dmBridge
JavaScript
1
star