🌊
[Flutter]Stateについての考察
Stateについて
- Stateの考え方には二種類あるよ
- App StateとEphemeral Stateだよ
- Ephemeral Stateはシンプルなステートだよ
- 単一のWidgetで管理するステート。つまりそれはStatefullWidgetのことだよ
- StatefullWidgetはリビルドされることもあるから(例えば画面が変わったとき)、当然Stateはリセットされるよ
- そうではなくて、複数のWidget間で値を保持したりしたい場合はAppStateだよ
- これは例えば、ECアプリのカートの中身とかがいい例だよ
- AppStateを管理する方法の明確なルールはないよ
- アプリの複雑さとチームの経験値で決めるといいよ
- AppStateの管理方法にはこれだけいろいろあるよ
https://flutter.dev/docs/development/data-and-backend/state-mgmt/options
State管理の問題点
- flutterは宣言的にUIを構築するよ
- このデータをこう表示しろと宣言し、UIの表示はFrameworkに任せるよ
- だからUIに変更を加えるときも、変更しろと命令するのではなく、データが変更されたら、Frameworkが再描画してくれるよ。リアクティブだね
- ということは、Stateを管理するとき、データ自体は上位のコンポーネントで持たないといけないね
- WidgetA -> WidgetB -> WidgetC という構造があったとき、StateのデータはWidgetAが持っているべきだよ
- なぜならそうしないとWidgetBとWidgetCを変更できないからだよ。仮にWidgetCがStateのデータを持っていたら、WidgetAとWidgetBの状態は変更できないね
- では、WidgetCでWidgetAが持っているStateに値を追加したいときはどうしたらいいかな
- 最初に思いつくのは、WidgetAでStateに値を追加する関数を定義して、その関数をWidgetBへ渡し、さらにWidgetCへ渡すという方法だね
- でもこれだとWidgetの階層が複雑になればなるほど、関数を渡すのが大変だよね
- ここまではreactやらvueと全く同じ問題だよ
providerによるState管理
- その解決方法の、もっともシンプルな方法がproviderという方法だよ
- providerは3つの概念で構成されるよ
- ChangeNotifier
- ChangeNotifierProvider
- Consumer
ChangeNotifire
- ChangeNotifierでStateを管理するよ
class CartModel extends ChangeNotifier {
final List<Item> _items = [];
int get totalPrice => _items.length * 42;
void add(Item item) {
_items.add(item);
notifyListeners();
}
}
- ChangeNotifierを継承したクラスを定義して、そこでデータを持つよ
- データを変更するメソッドを定義したら、その中で notifyListerners()を呼び出すよ
- notifyListenrers()を呼び出すと、変更したことを通知するよ
ChangeNotifireProvider
- ChangeNotifierProviderで変更を受け取れるようにするよ
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CartModel(),
child: MyApp(),
),
);
}
- UIを定義するときに、変更を検知したい箇所をChangeNotifierProviderで囲ってあげるよ
- reduxのときとかもProviderで囲ったりしてたね
Consumer
- Consumerを使ってデータにアクセスするよ
return Consumer<CartModel>(
builder: (context, cart, child) {
return Text("Total price: ${cart.totalPrice}");
},
);
- NotifierProvider配下のComponent内でConsumerを使うと、Stateにアクセスできるよ
Discussion