provider_for_redux 1.1.2

  • Readme
  • Changelog
  • Example
  • Installing
  • 83

provider_for_redux #

With Provider you can inject your state, but it's your job to figure out how to update that state, structure the solution etc. In other words, Provider is a great alternative to InheritedWidget, and lets you do dependency injection, but you still need to do your own state management.

What people mean by “Provider as state management” is usually using Provider to do scoped model. But as a matter of fact, this is not the only possible architecture using Provider. You can use it with Bloc, Mobx, Redux and other.

This package lets you use it with AsyncRedux, the non-boilerplate version of Redux.

How to use it #

Please, read the async_redux documentation first, if you haven't already.

You should have learned that to use AsyncRedux the traditional way, you provide the Redux store to your app by wrapping it with a StoreProvider, and then using the so called "connector" widgets, like the MyHomePageConnector below:

@override
Widget build(BuildContext context) => 
    StoreProvider<AppState>(
        store: store,
        child: MaterialApp(
            home: MyHomePageConnector(),
        ));

Now, if you want to use AsyncRedux with Provider, simply remove the StoreProvider and use AsyncReduxProvider instead. Also, you won't need the connector widgets anymore, since you will use Provider instead.

For example:

@override
Widget build(BuildContext context) => 
    AsyncReduxProvider<AppState>.value( // Instead of StoreProvider.
        value: store,
        child: MaterialApp(
            home: MyHomePage(), // Instead of MyHomePageConnector. 
        ));

The AsyncReduxProvider widget above will expose the store, the state, and the dispatch method to its descendants:

  • The Redux store, of type Store. Get it like this: Provider.of<Store<AppState>>(context).

  • The store's state, of type AppState. Get it like this: Provider.of<AppState>(context).

  • The dispatch method, of type Dispatch. Get it like this: Provider.of<Dispatch>(context).

  • The async dispatch method, of type DispatchFuture. Get it like this: Provider.of<DispatchFuture>(context).

This is a complete example:

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key}) : super(key: key);

  int counter(context) => Provider.of<AppState>(context).counter;

  String description(context) => Provider.of<AppState>(context).description;

  VoidCallback onIncrement(context) =>
      () => Provider.of<Dispatch>(context, listen: false)(IncrementAndGetDescriptionAction());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Increment Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("You've pushed the button:"),
            Text('${counter(context)}', style: TextStyle(fontSize: 30)),
            Text('${description(context)}', style: TextStyle(fontSize: 15)),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: onIncrement(context),
        child: Icon(Icons.add),
      ),
    );
  }
}

Try running the Provider.of example.

Consumer #

You can use Provider's Consumer class to read from the store. For example:

return Consumer<Store<AppState>>(
  builder: (context, store, child) =>
      ...     
      Text('${store.state.counter}'),
      ...
      onPressed: () => store.dispatch(IncrementAction()),      
  ),

But it's easier if you use ReduxConsumer, which already gives you the store, the state, and the dispatch method:

return ReduxConsumer<AppState>(
    builder: (context, store, state, dispatch, child) => ...

This is a complete example:

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ReduxConsumer<AppState>(
      builder: (context, store, state, dispatch, child) => Scaffold(
        appBar: AppBar(title: Text('Increment Example')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text("You've pushed the button:"),
              Text('${state.counter}', style: TextStyle(fontSize: 30)),
              Text('${state.description}', style: TextStyle(fontSize: 15)),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => dispatch(IncrementAndGetDescriptionAction()),
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

Try running the ReduxConsumer example.

Selector #

You can use Provider's Selector class to read from the store, while preventing unnecessary widget rebuilds. For example:

return Selector<Store<AppState>, Tuple2<int, Dispatch>>(

   selector: (context, store) => 
      Tuple2(store.state.counter, store.dispatch),

   builder: (context, model, child) => 
      ...     
      Text('${model.item1}'),
      ...
      onPressed: () => model.item3(IncrementAction()),      
      ),

Your selector parameter must return a "model" which you can use to build your widget in the builder parameter. In the above example the model is a Tuple2 instance, but you can return any immutable object that correctly implements equals and hashcode. The widget will rebuild whenever the model changes.

But it's easier if you use ReduxSelector, which already gives you the store, the state, and the dispatch method:

return ReduxSelector<AppState, Tuple2<int, String>>(
    selector: (context, state) => Tuple2(state.counter, store.dispatch),

   builder: (ctx, store, state, dispatch, model, child) => 
      ...     
      Text('${state.counter}'),
      ...
      onPressed: () => store.dispatch(IncrementAction()),      
      ),

Try running the ReduxSelector with model example.

However, ReduxSelector also lets you return a List as the model. In case you do that, it will rebuild the widget whenever any of the items in the list changes:

return ReduxSelector<AppState, dynamic>(
    selector: (context, state) => [...],
    builder: (context, store, state, dispatch, model, child) => ...    

Using ReduxSelector with a list is the easiest way of all, since you just need to list all of the state parts that should trigger a rebuild.

Note: The builder gives you both the state and the model, and you can choose one of them to build your widget from. While using state is probably easier for you, it's also easier to accidentally use something you forget to add to the model, and then wonder why the Widget doesn't rebuild. So you have two options:

  1. Use only the model and have the compiler make sure there's no state you're using from outside of the model.
  2. Use state directly, and have yourself the responsibility to make you've listed everything you're using in the model.

This is a complete example:


Widget build(BuildContext context) {       
  return ReduxSelector<AppState, dynamic>(
     
      selector: (context, state) => [
                                    state.counter, 
                                    state.description
                                    ],

      builder: (context, store, state, dispatch, model, child) => 
         Scaffold(
             appBar: AppBar(title: Text('Increment Example')),
             body: Center(
               child: Column(
                 mainAxisAlignment: MainAxisAlignment.center,
                 children: [
                   Text("You've pushed the button:"),
                   Text('${state.counter}', style: TextStyle(fontSize: 30)),
                   Text('${state.description}', style: TextStyle(fontSize: 15)),
                 ],
               ),
             ),
             floatingActionButton: FloatingActionButton(
               onPressed: () => dispatch(IncrementAndGetDescriptionAction()),
               child: Icon(Icons.add),
             )));
}

Try running the ReduxSelector with list example.

Migrating #

When you use AsyncReduxProvider you will notice that both Provider and AsyncRedux's StoreConnector will work simultaneously. You can mix and match both of them, as desired, or slowly migrate between them.


Special thanks for Remi Rousselet, main author or Provider, for helping me with ideas and making suggestions.

The Flutter packages I've authored:

My Medium Articles:

My article in the official Flutter documentation:

---
Marcelo Glasberg:
https://github.com/marcglasberg
https://github.com/marcglasberg
https://twitter.com/glasbergmarcelo
https://stackoverflow.com/users/3411681/marcg
https://medium.com/@marcglasberg

[1.1.2] - 2020/05/19 #

  • Docs improvement.

[1.1.1] - 2020/05/01 #

  • DispatchFuture in Provider.of.

[1.1.0] - 2020/01/08 #

  • Depends on Provider 4.0.

[1.0.6] - 2019/09/17 #

  • Depends on newest async_redux.

[1.0.2] - 2019/09/16 #

  • Docs improvement.

[1.0.1] - 2019/09/15 #

  • Initial commit.

example/lib/main.dart

import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_for_redux/provider_for_redux.dart';

import 'actions.dart';
import 'app_state.dart';

// Developed by Marcelo Glasberg (Aug 2019).
// For more info, see: https://pub.dartlang.org/packages/provider_for_redux

Store<AppState> store;

/// This example shows how to use `Provider.of` to access the Redux store.
///
void main() {
  var state = AppState.initialState();
  store = Store<AppState>(initialState: state);
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => AsyncReduxProvider<AppState>.value(
        value: store,
        child: MaterialApp(home: MyHomePage()),
      );
}

///////////////////////////////////////////////////////////////////////////////

/// The screen has a counter, a text description, and a button.
/// When the button is tapped, the counter will increment synchronously,
/// while an async process downloads some text description.
class MyHomePage extends StatelessWidget {
  MyHomePage({Key key}) : super(key: key);

  int counter(ctx) => Provider.of<AppState>(ctx).counter;

  String description(ctx) => Provider.of<AppState>(ctx).description;

  VoidCallback onIncrement(ctx) =>
      () => Provider.of<Dispatch>(ctx, listen: false)(IncrementAndGetDescriptionAction());

  @override
  Widget build(BuildContext ctx) {
    return Scaffold(
        appBar: AppBar(title: Text('Increment Example (1)')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text("You've pushed the button:"),
              Text('${counter(ctx)}', style: TextStyle(fontSize: 30)),
              Text('${description(ctx)}', style: TextStyle(fontSize: 15)),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: onIncrement(ctx),
          child: Icon(Icons.add),
        ));
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  provider_for_redux: ^1.1.2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:provider_for_redux/provider_for_redux.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
74
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
80
Overall:
Weighted score of the above. [more]
83
Learn more about scoring.

We analyzed this package on Jul 8, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package does not support Flutter platform android

Because:

  • package:provider_for_redux/provider_for_redux.dart that imports:
  • package:async_redux/async_redux.dart that imports:
  • package:async_redux/src/local_persist.dart that imports:
  • package:path_provider/path_provider.dart that imports:
  • package:path_provider_linux/path_provider_linux.dart that declares support for platforms: linux

Package does not support Flutter platform ios

Because:

  • package:provider_for_redux/provider_for_redux.dart that imports:
  • package:async_redux/async_redux.dart that imports:
  • package:async_redux/src/local_persist.dart that imports:
  • package:path_provider/path_provider.dart that imports:
  • package:path_provider_linux/path_provider_linux.dart that declares support for platforms: linux

Package does not support Flutter platform macos

Because:

  • package:provider_for_redux/provider_for_redux.dart that imports:
  • package:async_redux/async_redux.dart that imports:
  • package:async_redux/src/local_persist.dart that imports:
  • package:path_provider/path_provider.dart that imports:
  • package:path_provider_linux/path_provider_linux.dart that declares support for platforms: linux

Package does not support Flutter platform web

Because:

  • package:provider_for_redux/provider_for_redux.dart that imports:
  • package:async_redux/async_redux.dart that imports:
  • package:async_redux/src/local_persist.dart that imports:
  • package:path_provider/path_provider.dart that declares support for platforms: android, ios, linux, macos

Package does not support Flutter platform windows

Because:

  • package:provider_for_redux/provider_for_redux.dart that imports:
  • package:async_redux/async_redux.dart that imports:
  • package:async_redux/src/local_persist.dart that imports:
  • package:path_provider/path_provider.dart that declares support for platforms: android, ios, linux, macos

Package not compatible with SDK dart

Because:

  • provider_for_redux that is a package requiring null.

Health suggestions

Format lib/provider_for_redux.dart.

Run flutter format to format lib/provider_for_redux.dart.

Maintenance issues and suggestions

Use constrained dependencies. (-20 points)

The pubspec.yaml contains 1 dependency without version constraints. Specify version ranges for the following dependencies: async_redux.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.8.0 <3.0.0
async_redux any 2.12.3
flutter 0.0.0
path_provider ^1.4.4 1.6.11
provider ^4.0.1 4.2.0
Transitive dependencies
collection 1.14.12 1.14.13
file 5.2.1
intl 0.16.1
logging 0.11.4
meta 1.1.8 1.2.1
nested 0.0.4
path 1.7.0
path_provider_linux 0.0.1+2
path_provider_macos 0.0.4+3
path_provider_platform_interface 1.0.2
platform 2.2.1
plugin_platform_interface 1.0.2
process 3.0.13
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8
weak_map 1.2.1
xdg_directories 0.1.0
Dev dependencies
flutter_test