🌊

[Flutter]Stateについての考察

2021/02/16に公開

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つの概念で構成されるよ
  1. ChangeNotifier
  2. ChangeNotifierProvider
  3. 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