カテゴリー・パッケージ・リポジトリを検索

パッケージ詳細v8.0.0

provider_for_redux

Provider を Redux と併用できるようにします。非ボイラープレート版の Redux、AsyncRedux(https://pub.dev/packages/async_redux)を使用しています。

AndroidiOSWebmacOSWindowsLinux
30日間ダウンロード
118
いいね数
16
Pub ポイント
150

パッケージをインストール

flutter pub add provider_for_redux

パッケージ概要

公開情報に基づく選定ガイドです。参考としてご利用ください。

provider_for_redux は、軽量でボイラープレートのない Redux 実装である AsyncRedux を使用して、Provider と Redux のシームレスな統合を可能にします。開発者は依存関係のインジェクションに Provider のシンプルさを利用しつつ、Redux の予測可能な状態管理の利点も享受できます。このパッケージは、過度なコードを書かずに構造的な状態処理が必要なアプリに最適です。すべての主要プラットフォームをサポートし、非同期アクションおよび不変状態パターンを通じて状態の更新を簡略化します。

用途

  • スケーラブルな状態管理のための Provider と Redux の組み合わせ
  • 予測可能な状態フローで大規模なFlutterアプリを構築する
  • 複雑なアプリケーションにおける状態ロジックの簡素化
  • Provider のリアクティブウィジェットと AsyncRedux を使用する
  • 既存の Redux ロジックをより明確なパターンに移行する

主な機能

  • Provider を AsyncRedux と統合する
  • 従来のReduxと比較して、ボイラープレートを削減します
  • 非同期状態の更新をサポートします
  • 型安全な状態とアクションの処理を提供
  • すべての Flutter プラットフォームで動作します

適している対象

  • Redux に精通した開発者
  • 構造化された状態管理が必要なチーム
  • 依存関係の注入と状態制御の両方を必要とするプロジェクト
  • 複雑な状態ロジックを持つアプリ
  • Reduxのボイラープレートを避けたい人

注意事項

  • Provider および Redux の概念を理解している必要があります
  • 小さなアプリには複雑さを追加する可能性があります
  • AsyncRedux に依存しており、それ自体に学習曲線があります
  • 単純な状態の必要に推奨されません
  • 中〜大規模なプロジェクトに最適

元の README

関連するソースリポジトリの README 原文のスナップショットです。最新の内容はリポジトリをご覧ください。

pub package

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).

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.


By Marcelo Glasberg

glasberg.dev
github.com/marcglasberg
linkedin.com/in/marcglasberg/
twitter.com/glasbergmarcelo
stackoverflow.com/users/3411681/marcg
medium.com/@marcglasberg

My article in the official Flutter documentation:

The Flutter packages I've authored:

My Medium Articles:

カテゴリー

依存関係

依存関係一覧 3 件

ソースリポジトリ

このパッケージを使用しているプロジェクト

データはまだありません。