flutter_reduxのREADMEのexampleをもうちょっとimmutableにしながら学習する
概要
flutter_redux
を使ってFlutterにReduxを導入します。
https://pub.dev/packages/flutter_redux のREADMEをベースに解説するだけ…のつもりだったのですが、exampleがimmutableな書き方でなかったのでちょくちょく修正しながらやってみる記事です。
成果物はincrementするカウンターです。
この記事で説明しないこと
- Reduxとは何か
実際にやってみた
雑にセクションごとに解説していきます。
新規プロジェクト作成とインストール
flutter_redux $ flutter create .
pubspec.yamlにflutter_reduxを追加します。
dependencies:
flutter_redux: ^0.6.0
Stateの定義
react reduxにおいてはStateはplain old javascript objects (pojo)でした。
しかし、flutter_reduxののサンプルではstateがただのintとして名前も付けられずに定義されており、これではスケールしないのでとりあえずクラスで定義します(@immutable
も付けます)。
@immutable
class State {
State({this.counter = 0});
int counter;
State copyWith({counter}) {
return State(
counter: counter ?? this.counter,
);
}
}
copyWith
メソッドが生えていますが一旦気にしないでください。Reducerの項で解説します。
Actionの定義
何のことはない。enumで定義します。
enum Actions { Increment }
Reducerの定義
Reducerは(oldState, action) => newState
な純粋関数なのでそのまま実装します。
State counterReducer(State state, Object action) {
if (action == Actions.Increment) {
state.counter++;
}
return state;
}
…と言いたいところなのですが、サンプルではこのようにstateのpropertyを直接編集していたりifで書いてたりします。
immutableでない点とケースが完全に網羅されているか分からない点に違和感があるので以下のように書き直します。
State counterReducer(State state, Object action) {
switch (action) {
case Actions.Increment:
return state.copyWith(counter: state.count + 1);
break;
default:
return state;
}
}
Stateに生やしたcopyWith()
はここで使います。
stateのプロパティを編集して新しいstate(かのように)返す関数は純粋関数とは呼べないため、copyWith
を使って書き換えたい項目だけ更新して新しいインスタンスを返させるようにしました。
ここまではただ単にreduxの世界の話です。ここまででuni-directionalなデータフローは完成しました。
(とりあえずimmutableっぽくなるように実装したのですが、exampleがこのような書き方になってる理由をご存じの方がいらっしゃったらご教授ください…🙏 ざっと調べた感じ実際にflutterでreduxを実装する時はこのように実装する人が多いように見えております…)
UIを実装する
ここから実際にstateを表示するUIと、ActionをdispatchするUIを実装します。
storeを定義する
storeはstateを持ち、stateを変更する唯一の方法であるaction
をdispatch
する関数を持ちます。
要するにstateとactionを結びつける役割を持ちます。
参考:
A store holds the whole state tree of your application. The only way to change the state inside it is to dispatch an action on it.
https://redux.js.org/api/store#store
storeはアプリケーションのあらゆる箇所で必要とされるため、main()
の直下で定義します。
void main() {
final store = Store<State>(counterReducer, initialState: State(count: 0));
runApp(
StoreProvider<State>(
store: store,
child: MyApp(title: 'Flutter Redux Demo'),
),
);
}
カウンターとボタンの実装
下位Widgetへstoreを渡す準備ができたので実際にUIを作っていきます。
カウンター部分を表示するUIはstateの変更を監視したいため、都度UIをrebuildするStoreConnector()を、クリックする度にカウンターをincrementするボタンのUIは、actionをdispatchする機能を持たせますがstateの変更をリッスンする必要がないためStoreProvider.of()を使います。
class MyApp extends StatelessWidget {
MyApp({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: new ThemeData.dark(),
title: title,
home: Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('you have pushed the button this many times'),
StoreConnector<State, int>(
converter: (store) => store.state.count,
builder: (context, count) {
return Text(
count.toString(),
style: Theme.of(context).textTheme.display1,
);
},
),
]),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () =>
StoreProvider.of<State>(context).dispatch(Actions.Increment),
),
),
);
}
}
StoreConnectorについて
StoreConnectorはstoreの変更を検知してwidgetをrebuildしますが、微妙に使い方が分からなかったので補足します。
-
StoreConnector<S, ViewModel>
は1つ目のジェネリック型にはstateの型を、2つ目にはconverterの返り値でありbuilderの第2引数になるViewModelの型を書きます。ViewModelと言うとピンとこないですが、要するに最終的にbuilderで使いたいようにstoreを加工してやった後の型を書けば良いです。今回で言うとカウンターの数字があればよいのでintになります。 - converterにはの第1引数にstoreを受けてViewModelを返す関数を書きます。
- builderにはBuildContext,ViewModelを引数にウィジェットを返すbuilder関数を書きます。
StoreProvider.ofについて(actionのdispatch)
StoreProvider.of<State>(context)
でstoreが取れるので、これに生えたdispatchの引数にActionを渡せばreducerがstateを更新してくれます。
補足: StoreProviderは変更をリッスンしないと思っているのですが、間違っていればご指摘ください…🙇
https://pub.dev/documentation/flutter_redux/latest/flutter_redux/StoreProvider-class.html
まとめ
出来ました。
flutter_redux自体、dartの性質も相まってライブラリとしてはそこまでimmutablityを推していないのかな…?と感じました。
実際、公式のtodo exampleを除くと自前でcopyWithを実装していたりするのでこれがスタンダードなのかな…?と思いました。
次は非同期処理かもう少し大きな規模のアプリを作りたいと思います。
Author And Source
この問題について(flutter_reduxのREADMEのexampleをもうちょっとimmutableにしながら学習する), 我々は、より多くの情報をここで見つけました https://qiita.com/canisterism/items/9c721b925836c517435f著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .