はじめに
Flutterを網羅的に学習するにあたってRoadmapを使って学習を進めることにしました。
この記事では、Flutter初学者やこれからFlutterを学習し始める方に向けて、状態管理についてまとめています。
RoadmapはFlutterだけでなく、他の言語やスキルのロードマップも提供されており、何から学習して良いか分からないと悩んでいる方にとって有用なサイトになっています。
ぜひRoadmapを利用して学習してみてください。
Roadmapとは
簡潔に言えば、Roadmap.shは学習者にとってのガイドブックであり、学習の方向性を提供する学習ロードマップサイトです。
初心者から上級者まで、ステップバイステップでスキルを習得するための情報が提供されています。
学習の進め方が分かりやすく示されているだけでなく、個々の項目に参考資料やリソースへのリンクも提供されているので、学習者は目標を設定し、自分自身のペースで学習を進めることができます。
State Management
FlutterロードマップState Managementでは以下の6つのサイトが紹介されています。興味のある方はぜひお読みください。
- State management in Flutter: https://docs.flutter.dev/data-and-backend/state-mgmt
- Intro to State Management: https://docs.flutter.dev/data-and-backend/state-mgmt/intro
- ValueNotifier class - Flutter: https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html
- ValuerNotifier & ValueListenableBuilder: https://medium.com/@avnishnishad/flutter-communication-between-widgets-using-valuenotifier-and-valuelistenablebuilder-b51ef627a58b
- ChangeNotifier class - Flutter: https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html
- Simple app state management: https://docs.flutter.dev/data-and-backend/state-mgmt/simple
状態管理とは
状態管理とは、アプリケーション内でデータの変更を追跡し、それに基づいてUIを更新するプロセスのことです。Flutter の UI は、状態 を build()
メソッドで参照しながら構築される設計になっています。そしてその状態の整合性が破綻しないように、「状態をどういうフローでどこに保持するか」、「どのように状態の変更を検知してリビルドするか」ということについて、ルールを決めて扱うということをひとことで 状態管理 と言います。
Flutterにおいて状態はstateと呼ばれ、state(状態)は大きく分けて2種類あります。
一つは Ephemeral state (ローカル状態 / エフェメラル状態)、もう一つは App state (共有状態) です。
Ephemeral state
とは、クラス間で共有しない、スコープが閉じたデータ(状態)のことです。例えば ToDoアプリのリストがあります。各ToDoアイテムは、それぞれが削除されるか完了するかなどの状態を持ちます。このToDoアイテムごとの状態は、そのウィジェット内で管理されるのが基本です。ToDoアイテムの削除ボタンを押すと、そのアイテムのローカルな状態が変更され、ウィジェットがリビルドされることで即座に反映されます。基本的に他のクラスから参照されることはないので Ephemeral state
と言えます。
一方 App State
とは、複数のクラス間で共有されるデータ(状態)のことです。例えばアプリ全体で共有される状態の例として、ユーザーの認証状態があります。ユーザーの認証状態はアプリ全体で共有され、それに基づいてUIや機能が制御されます。認証が必要な画面では、ユーザーがログインしているかどうかに基づいて適切な画面が表示されため App State
と言えます。
ChangeNotifier
ChangeNotifierとは、Flutter SDKに含まれている、リスナーに変更通知を提供し、監視可能にさせるためのクラスです。ChangeNotifierを extends
しているクラスは、インスタンスの中のメソッドが実行され、状態の変更が発生した際に notifyListeners
でUIの更新をトリガーすることができます。
- notifyListeners(): 登録されたリスナーに変更を通知するメソッドです。状態が変更されたときにこのメソッドが呼び出されます。
サンプルコード
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ChangeNotifierProvider.value(
value: CounterModel(),
child: const MyHomePage(),
),
);
}
}
class CounterModel extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
final counterModel = context.watch<CounterModel>();
return Scaffold(
appBar: AppBar(
title: const Text('ChangeNotifier Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Counter: ${counterModel.counter}',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
counterModel.increment();
},
child: const Text('+1'),
),
],
),
),
);
}
}
-
CounterModel クラス:
-
ChangeNotifier
を継承したモデルクラスです。このクラスで状態を保持し、変更を通知します。 -
_counter
プロパティでカウンターの値を格納します。 -
increment
メソッドはカウンターを増やし、notifyListeners
メソッドで状態(counter
)の変更を通知します。
-
-
MyHomePage クラス:
- カウンターの現在の値を表示し、増加ボタンがあるウィジェットです。
-
context.watch<CounterModel>()
でCounterModel
インスタンスを取得し、その変更を購読します。
-
ChangeNotifierProvider:
-
ChangeNotifierProvider
はCounterModel
クラスを指定し、子ウィジェットでその変更を監視できるようにします。
-
ValueNotifier
ValueNotifier とは、 ChangeNotifier
を簡略化した状態管理クラスです。単一の値の変更を通知し、それに応じてリスナーが処理を実行できるようになります。 ChangeNotifier
とは異なり、 ValueNotifier
は notifyListeners
メソッドを持たず、代わりに value
プロパティを通じて現在の値にアクセスすることができます。
ValueNotifierは ChangeNotifier
を継承(extends)し ValueListenable
を実装(implements)したクラスです。 ChangeNotfier
を継承しているので、 ChangeNotifier
と同じように使うことができますが、状態変数は一つしか持ちません。
メリット
-
シンプルな値の管理: 状態変数が一つに強制されるので、状態管理クラスがシンプルに保たれます。そのため、単一の値の変更を管理するのに適しています。
-
簡潔な記述:
ValueNotifier
は、ChangeNotifier
よりもメソッドが少なく、直感的に使用することができます。ChangeNotifier
にはnotifyListeners
メソッドがあり、手動で呼び出す必要がありますが、ValueNotifier
は直接、値を更新することで変更を通知するため、notifyListeners
の呼び忘れによるバグを防ぐことができます。
デメリット
-
複雑な状態管理: 複雑な状態管理が必要な場合、
ValueNotifier
は単一の値しかサポートしていないため、十分な柔軟性がありません。 -
複数のリスナー:
ValueNotifier
は複数のリスナーをサポートしていますが、複数のリスナーが同じイベントに対して異なる処理を実行する必要がある場合、ChangeNotifier
の方が適しています。 -
Providerとの統合:
ChangeNotifier
はProvider
パッケージとの統合が容易で、多くの状態管理ライブラリで使用されている、一方で、ValueNotifier
はProvider
との統合が限定的です。
ValueListenableBuilder
ValueListenableBuilder とは、 ValueListenable
オブジェクト に登録された値を監視し、その値の変更に応じて、ウィジェットツリーを再構築する、Flutterで提供されているウィジェットのことです。非同期処理もサポートしており、 Future
や Stream
といった非同期イベントの監視にも利用できます。
主なプロパティ
-
valueListenable:
ValueListenable
のオブジェクトです。これが変更されると、builder
関数が呼び出されます。 -
builder: ウィジェットを構築するコールバック関数です。第二引数には
valueListenable
に登録されている現在の値が渡され、これに基づいてウィジェットが再構築されます。 -
child(オプション): オプションの
child
ウィジェットです。このプロパティは静的な部分で、再構築される必要がない場合に使用されます。
サンプルコード
import 'package:flutter/material.dart';
class CounterModel extends ValueNotifier<int> {
CounterModel(int value) : super(value);
void increment() {
value += 1;
}
}
class MyHomePage extends StatelessWidget {
final CounterModel counterModel;
MyHomePage({Key? key, required this.counterModel}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ValueNotifier Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ValueListenableBuilder<int>(
valueListenable: counterModel,
builder: (context, value, child) {
return Text(
'Counter: $value',
style: const TextStyle(fontSize: 24),
);
},
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
counterModel.increment();
},
child: const Text('+1'),
),
],
),
),
);
}
}
void main() {
runApp(
MaterialApp(
home: MyHomePage(
counterModel: CounterModel(0),
),
),
);
}
-
CounterModel クラス:
-
ValueNotifier<int>
を継承しているクラスです。これはint
型の値を保持するValueNotifier
として機能します。 -
increment
メソッドで、現在の値(value)に1を加えています。
-
-
MyHomePage クラス:
-
counterModel
プロパティで、CounterModel
のインスタンスを受け取ります。 -
ValueListenableBuilder
を使用して、counterModel
の値の変更を検知し、UIを更新しています。ボタンが押されたときにcounterModel.increment()
を呼び出して値を増加させています。
-
まとめ
-
状態管理 (State Management):
アプリケーション内でデータの変更を追跡し、それに基づいてUIを更新するプロセスのことです。Ephemeral state(ローカル状態)とApp state(共有状態)があり、UI構築において状態の整合性を保つための管理が重要になります。 -
ChangeNotifier:
Flutterに含まれる状態管理クラスで、リスナーに変更通知を提供し、UIの更新をトリガーするクラスになります。notifyListeners
メソッドを使用して変更を通知し、値を更新します。 -
ValueNotifier:
ChangeNotifier
を簡略化した状態管理クラスで、単一の値の変更を通知します。シンプルで簡潔な記述が可能でValueListenableBuilder
を使用して値の変更を検知し、UIを再構築します。
状態管理はアプリケーション開発において重要な概念であり、適切な方法を選択することでアプリケーションのパフォーマンスやメンテナンス性を向上させます。 ChangeNotifier
と ValueNotifier
はシンプルで小規模なアプリケーションに適しているため、必要に応じてより高度な状態管理パターンやライブラリを活用することが重要になってきます。
参考資料