最近Flutter調べてるんだけど、
Flutterの状態管理ライブラリって何だろうとか
ちょっと気になったから調べてみたよ
僕はFlutterのこと全然わからないし使ったこともないから正しいかどうかはわからないよ
Flutterの状態管理を調べていくと、
ReactのRecoilに似てるってあったからこれも調べたよ
メモだから一緒にしとくね
Flutterの状態管理
いくつか読んでみたけどこの記事がわかりやすかったよ
https://zenn.dev/heyhey1028/articles/78ccea1534dcd4
この記事によるとFlutterの状態管理ライブラリは
BLoC -> Provider -> Riverpod
という移り変わりらしいね
あとProviderとRiverpodはバケツリレーを解消するためのDIライブラリのようなもので、
実際に状態管理そのものをするわけではないらしいね
だからProviderやRiverpodは別の状態管理ライブラリと組み合わせて使うみたい。
こんな感じ
「Provider + ChangeNotifier」
「Riverpod + StateNotifier」
ChangeNotifier と StateNotifierのざっくりとした違いは
ChangeNotifierのほうが柔軟で自由で
StateNotifierのほうがシンプルらしいよ
どうやら今の流行はRiverpod+StateNotifierみたいだよ
これがRecoilに似てるらしいね
つまりFlux系らしいよ
https://developers.cyberagent.co.jp/blog/archives/36149/
Flutter Hooksっていうのが相性良いらしいからこれも使うみたいだね
だからよくこういう風に書いてあるよ
StateNotifier + Flutter Hooks + Riverpod
freezed っていうのも使うみたい
これは名前の通りにimmutableなクラスの扱いなどを楽にするライブラリなんだって
BloC
BloCProviderっていうので囲んだ中でBloCが管理する状態にアクセスできるよ
入れ子にしたり、必要な部分だけを囲むこともできるし、
アプリ全体を囲むこともできるみたいだよ。
https://bloclibrary.dev/#/recipesflutterblocaccess?id=global-access
BloCは複雑そうに見えるけど結構シンプルで、
UIイベントをBloCに送ると、BloCが状態を更新するよ
UIは状態を監視してるから、UIも更新されるという感じ
EventとStateを定義して、
そのEventでStateを更新する関数をBloC内に書くっていう感じ
つまりBloCはReducerなんだね
FluxとかMVIにも似てるね
雰囲気はこんな感じ
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
ExampleBloc() : super(ExampleState(value: "INIT"));
@override
Stream<ExampleState> mapEventToState(ExampleEvent event) async* {
if (event is FooEvent) {
yield ExampleState(value: "FOO");
} else {
yield ExampleState(value: "OTHER");
}
}
}
//Widget内でこうやって使う
BlocBuilder<ExampleBloc, ExampleState>(
builder: (context, state) => Text(state.value),
),
BloCのライブラリには
Cubitっていうのもあってこれを使うのも便利そうだよ
公式のチュートリアルでも使ってるからこれを使うのが普通っぽいね
Cubitを使うとMVVMっぽくなるよ
どういうことかというとEventを定義するんじゃなくて、
BloCにメソッドを作ってそこで状態を更新するって感じ
シンプルでいい感じだね
雰囲気はこんな感じ
class ExampleCubit extends Cubit<ExampleState> {
ExampleCubit() : super(ExampleState(value: "INIT"));
void foo() => emit(ExampleState(value: "FOO"));
void bar() => emit(ExampleState(value: "OTHER"));
}
ChangeNotifier
ChangeNotifierも結構シンプルな感じだよ
基本的にChangeNotifierを継承したクラスに、
状態のプロパティとメソッドを作ってそこで状態を更新するよ
UIでChangeNotifierを監視できるから、
ChangeNotifierの中にある状態にアクセスできるよ
ChangeNotifierのメソッド内でnotifyListeners()を呼び出すことで更新を通知できるよ
通知するかどうかをメソッド内で決めれるからかなり柔軟に使えそうだね
状態の更新によって通知されるわけじゃないから、
状態がmutableでもimmutableでも大丈夫だよ
一つのChangeNotifierの中に複数状態を定義することもできそうだね
雰囲気はこんな感じ
class ExampleStateChangeNotifier extends ChangeNotifier {
ExampleStateChangeNotifier() : state = ExampleState();
ExampleState state;
void changeState() {
state = ExampleState()
notifyListeners();
}
}
BloCのCubitとかMVVMに似てるね
Providerとかを使ってこれをWidgetツリーに流し込んでいく感じ
StateNotifier
StateNotifierは基本的にはChangeNotifierと同じだよ
ただ状態の持ち方と更新の通知がちょっと違うよ
StateNotifierにはあらかじめstateっていうプロパティが入ってて
これに値を入れることで通知も飛ばせちゃうって物だよ
notifyListeners()を呼び出す必要もないからシンプルだね
雰囲気はこんな感じ
class ExampleStateNotifier extends StateNotifier<ExampleState> {
ExampleStateNotifier() : super(ExampleState());
void changeState() {
state = ExampleState()
}
}
Provider
Provider自体は状態管理はなくて基本的には
バケツリレーを回避するためのライブラリっぽいよ
ProviderはReactのContextみたいに階層で管理するから弱点がいくつかあるよ
ネストになっちゃったり、
別階層からアクセスできないし、アクセスしようとしたら実行時エラーしちゃったり
Providerが必要なくなった時の破棄が大変だったりするんだって
あと同じ型のProviderを複数同時に利用することができないらしいよ
一つのタイプについて一つのProviderしか使えないんだって
https://medium.com/flutter-jp/state-1daa7fd66b94
でもすごいシンプルなのでライブラリの中で使われてたりしたりするよ
雰囲気こんな感じ
// これで囲む
Provider(
create: (_) => ExampleState(),
child: ...
)
//囲まれたところでこうやると取得できる
context.read<ExampleState>()
Riverpod
RiverpodはProviderの作者が弱点を克服するために作ったものなんだって
階層ではなくグローバルスコープに状態を置くので階層のデメリットがないらしいくて
参照がゼロになったら破棄してくれたりするんだって
StateNotifierはもちろんいろいろなProviderがあるみたい
https://riverpod.dev/docs/providers/state_notifier_provider
雰囲気こんな感じ
// アプリをProviderScopeで囲む
void main() {
runApp(
const ProviderScope(
child: ExampleApp(),
),
);
}
// グローバルスコープに状態とかのProviderを宣言する
final exampleStateProvider = StateNotifierProvider(
(_) => ExampleStateNotifier()
);
//ConsumerWidgetのbuildでこうすると取得できる
ref.watch(exampleStateProvider);
//Hooksを使うとこんな感じで書けてよさそう!
//HookConsumerWidget内
useProvider(exampleStateProvider);
ReactのRecoilに似てるらしいよ
Riverpod 2.0
Riverpod2.0からはNotifierっていうのが内蔵されたから
これを使えばRiverpodだけで状態の変更の通知までできるよ
NotifierはStateNotifierにすごく似てて
stateプロパティを更新することで変更が通知されるよ
こんな感じ
class ExampleNotifier extends Notifier<ExampleState> {
@override
ExampleState build() => ExampleState();
void changeState() => state = ExampleState();
}
// Providerの宣言
final exampleNotifierProvider = NotifierProvider(ExampleNotifier.new);
riverpod_generatorを使うとNotifierを自動生成することもできて便利だよ
Notifierを自動生成するとProviderも生成してくれるよ
@riverpod
class ExampleNotifier extends _$ExampleNotifier {
@override
ExampleState build() => ExampleState();
void changeState() => state = ExampleState();
}
// exampleNotifierProviderが自動生成されるよ
Recoil
JavascriptというかReactの状態管理ライブラリ
これもFacebookが作ってるんだって
ReactといえばReduxな感じするよね
Reduxが単一で状態を管理するけど、
RecoilはFluxみたいにバラバラで状態を管理する
グローバルで使えるuseStateって感じ
あと非同期通信に対応してるらしいよ
なんでこれを使うかっていうと
useStateのバケツリレーとか
useContextのプロバイダーのネスト問題を解決するため
Reduxとかほかのライブラリと目的自体は同じだね
atomとかselectorとか用語があるみたい
atomっていうのがStateのことっぽくて
selectorっていうのがReducerみたいな感じで
atomを編集した値を返せるもの
ただselectorも状態の一種らしくて状態を計算した後の状態って感じだよ
なので紐づいてる(計算元)状態が更新されたらselectorも更新されるらしいよ
紐づいてる状態が更新されてもselectorの値が更新されないなら再レンダリングはされないんだって
その辺はさすがしっかりしてるね
familyっていうのもあるらしいね
atomのdefaultを使用時に決めれるように宣言時に引数を渡せるらしいよ
雰囲気こんな感じ
// グローバルスコープにatom(状態)を宣言
const exampleState = atom({
key: "exampleState",
default: [],
});
export const ExampleComponent = () => {
const state = useRecoilState(exampleState);
return (
<>
{state}
</>
);
};
Jotai
Recoilに似てるよ
とても軽いことが特徴
Recoilをさらにシンプルにした感じ
Mobx
Recoilに似てるよ
違いとしては、RecoilはReactに依存してるけど
MobXは依存してないから、React以外のプロジェクトでも使えて、Flutterとかでも使えるんだって
https://lealog.hateblo.jp/entry/2020/05/17/021656