Flutterの状態管理方法考える ① -前提知識-

こんにちは、株式会社Pentagonでアプリ開発をしている山田です。
https://pentagon.tokyo

開発業務をしていると、チームや現場によってFlutterの状態管理の方法が違っていたりします。また自分でもその辺りを調べていると、その状態管理の方法が多い事に気づきました。

その状態管理とはについても辺りもまとめつつ、今回業務で使用しているFlutter_hooks+hooks_riverpodについてもまとめていきたいと思います。

かなり全体として長くなると思いますので分割してまとめて行こうかと思います。

目次

状態管理(StateManagement) を学ぶにあたっての基礎知識

以下がState Managementの公式ドキュメントになっています。

State management

以前からAndroid SDKやiOS UIKitでの開発(左記のフレームワークを使用している)を行っているのであれば、この部分について新しい視点や知識を入れる必要はありません。

もし、Flutterが初めてであれば、以下のことを覚えておいてください。

  • Flutter は宣言的(declarative)です。つまり、アプリの状態を反映したUIを構築します。イメージとしては以下の図です。
  • アプリの状態が変わるときはUIの再描画も行われます。
  • UIの任意の状態に対してコードパスは一つしかありません。

これらのことを踏まえ次の項目に続きます。

Differentiate between ephemeral state and app state(一時的な状態とアプリの状態の違いについて)

このタイトルから、私はサッパリわかりませんでした(笑)

ここの項目では、一時的な状態(ephemeral state)とアプリの状態(app state) について、それぞれどの様に管理するかが書かれています。

一時的な状態(ephemeral state)

この一時的な状態はローカル状態あるいはUI状態とも呼ばれており、1つのWidgetで収めることができます。

曖昧ですよね(汗)。例を示します。

  • PageViewの現在のページ
  • 複雑なアニメーションの現在の進行状況
  • BottomNavigationBar の現在の選択されたタブ

まだ曖昧ですかね?

簡単に言うと、難しく状態管理技術(FlutterHookやReduxやScopedModelといった難しい技術)を使っていない、StatefulWidgetだけで書かれた1画面のコードのことです。

コードで書くと次のとおりです。

class MyHomepage extends StatefulWidget {
  const MyHomepage({Key? key}) : super(key: key);

  @override
  _MyHomepageState createState() => _MyHomepageState();
}

class _MyHomepageState extends State<MyHomepage> {
  int _index = 0;

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      currentIndex: _index,
      onTap: (newIndex) {
        setState(() {
          _index = newIndex;
        });
      },
      // ... items ...
    );
  }
}

これはよく見るStatefulWidgetのコードです。

この中の_index が一時的な状態の例です。
他の画面から_indexにアクセスする必要がなく、再起動されても 0 にリセットされても問題ありません。

アプリの状態(app state)

一時的でない状態が、アプリの状態です。

これは、アプリのセッションの間で情報を維持したい、持っておきたい状態です。これがアプリの状態、もしくは共有された状態と呼びます。

これは具体として簡単ですね。

  • ユーザー設定画面
  • ECのカート内情報
  • ログイン情報

このアプリの状態を管理することが大変難しく、今回のゴールとなっている状態管理の技術を選定する必要があります。

明確なルールは無い

そう言われると、少し困りますが。(汗)

はっきり言って、StatesetState() で全てのアプリの状態を管理できます。実際、皆さんが初めてFlutterのプロジェクトを開いた際に出てくるサンプルコード(カウントコード)もStatesetState() のみで書かれています。

逆もまた然りです。先ほど書いた_indexの値を外部のクラスから値を取得するなら、_indexはアプリの状態となります。

つまり、一時的な状態とアプリの状態を区別するため、そして採用するための普遍的なルールは存在しません。
また、開発を行っていく中で、アプリが肥大化して行くと、一時的な状態からアプリの状態になることもあります。

もし、どの状態を使うべきか迷った時には次のダイアグラムを参考にしてください。

Redux の開発者、Dan AbramovがReactのsetState vs Redux's store について以下の様に回答しています。

The rule of thumb is: do whatever is less awkward.
経験則として、厄介で無い方を選択する。
http://https://github.com/reduxjs/redux/issues/1287#issuecomment-175351978

面倒にならないために、どっちか選んで使ってということですかね。

まとめ

Flutterには、2つの概念的な状態があります。

  • StateとsetState()を使用して実装し、ひとつのローカルWidgetを用いた一時的な状態
  • それ以外のアプリの状態

この2つの状態はアプリの複雑さや自身の好みにより使い分けます。

続く。。。。

採用情報はこちら
目次