すらぼうの開発ノート

モバイルアプリエンジニアのメモ

【Flutter】providerで状態管理をする方法

本エントリではproviderを用いた状態管理を以下の流れで説明する。



状態管理とは

本エントリで状態管理とはアプリケーション内の変数の値をどのように保存・更新・読み取り・削除するかを指す。

アプリ開発を進めると徐々にコードの規模が大きくなる。 その際に状態管理を適当に行っていると、「この時にアプリは何を表示している?」「この変数はいつ値が変更される?」などの問題が発生し保守性や開発効率の低下を引き起こす。

そのため効率的にアプリ開発を行う上で状態管理は必須になる。


providerとは

概要

providerウィジェットツリー上の子ウィジェットに状態変化を通知するためのパッケージ。 Flutterにおいて状態管理を行うためのツールとしてはかなりメジャーで実際の開発現場でも頻繁に使用されるため是非慣れておきたい。

pub.dev

InheritedWidgetをラップしたパッケージで、変化した状態に応じて必要なウィジェットのみをリビルトすることができる。

Flutter公式サイトのcookbookにもproviderの利用方法を説明したページがあるので適宜参照してほしい。

docs.flutter.dev

使用方法

データの準備

providerで管理するデータはChangeNotifierを継承したクラスを作成する。 例として以下のような記述になる。

ポイントは

  • データ用クラスはChangeNotifierを継承する
  • 変数の値を変更後にnotifyListeners()をコールする

の2点。

データの共有

「データの準備」で作成したクラスは

  • MultiProvider
  • ChangeNotifierProvider

を用いることでchildプロパティに指定した子ウィジェットに共有することができる。

具体的には以下のように記述する。

データの取得

データを取得する際には以下を利用する。

  • context.watch<T>()
  • context.read<T>()
  • context.select<T>()

各メソッドの違いは以下。

メソッド名 説明
context.watch<T>() Tのデータを取得と監視。notifyListeners()によって変更が通知される
context.read<T>() Tのデータを取得のみ。notifyListeners()によって変更は通知されない
context.select<T>() Tのデータで指定したプロパティのみ取得と監視notifyListeners()によって変更が通知される

context.select<T>()はTのプロパティの一部のみ変更を受け取りたい場合に用いる。 そのため再ビルドの回数を抑えてアプリのパフォーマンスを上たい場合に使用する。


サンプルアプリ

providerを用いたシンプルなTodoアプリを作成する。

動作

providerサンプルアプリ動作

コード

コードは分量が多いため以下のリポジトリを参照。 興味があればcloneして実際に操作をしていただきたい。

github.com

解説

フォルダ構成

フォルダは以下の構成。

フォルダ構成

各ファイルの役割を簡単に説明する。

ファイル名 役割
main.dart アプリのエントリーポイント
todo_model.dart Todoアイテム用のモデル
todo_provider.dart 表示するTodoインスタンス一覧の管理用provider
home_page.dart Todoインスタンス一覧を表示するためのページ作成用
create_page.dart Todoインスタンス作成ページ用
list_item.dart home_page.dartで各Todoインスタンスを表示するためのウィジェット作成用

ポイント

本サンプルアプリはproviderを用いて

の機能を実装している。一つずつ詳細に解説する。

Todoインスタンスの共有

ウィジェットツリーにてHomePage以下のウィジェットに対してTodoインスタンスを共有するため、MultiProviderHomePageをラップする。

Todoインスタンスの一覧への追加

Todoインスタンスを一覧に追加するためにaddTodo()メソッドを作成した。

_todos.add(todo)TodoProviderで管理しているTodoインスタンス一覧に追加を行い、その後notifyListeners()で変更の通知を行っている。

Todoインスタンス一覧の取得

Todoインスタンス一覧の取得はhome_page.dartlist_item.dartで以下のように行っている。

変更を監視したいので、context.watch<TodoProvider>()で一覧を取得している。

Todoインスタンスの一覧からの削除

Todoインスタンスを一覧から削除するためremoveTodoAtIndex(int index)メソッドを作成。 削除の際には_todosリスト上のインデックスが必要なので引数で取得する。

リストから対象のTodoインスタンスを削除後、notifyListeners()で変更の通知を行っている。 removeTodoAtIndex(int index)メソッドは以下のように使用している。

Todoインスタンスの更新

一覧上のTodoインスタンスを更新するためupdateIsDoneAtIndex(int index)メソッドを作成。 サンプルアプリではisDoneプロパティのみ更新する。

更新の際には_todosリスト上のインデックスが必要なので引数で取得する。

プロパティを更新後notifyListeners()で変更の通知を行っている。 updateIsDoneAtIndex(int index)メソッドは以下のように使用している。