Provider の概要と仕組み
Flutter アプリケーションにおいて、ウィジェットツリーを跨いでデータを共有し、状態の変化に応じて UI を更新することは重要な課題です。Provider は、Flutter 標準の InheritedWidget をラップしたライブラリであり、依存性注入と状態通知の仕組みを簡素化します。
このパッケージを利用することで、状態管理ロジックを UI コードから分離し、ChangeNotifier を介してリスナーであるウィジェットに効率的に更新を通知できます。これにより、props のバケツリレー現象を防ぎつつ、必要な箇所のみを再構築することが可能になります。
実装手順
Provider をプロジェクトに導入するには、まず依存関係の追加から始まります。pubspec.yaml に以下の記述を追加してください。
dependencies:
flutter:
sdk: flutter
provider: ^6.0.5
次に、アプリケーションの状態を保持するモデルクラスを定義します。このクラスは ChangeNotifier をミックスインし、状態が変更された際に notifyListeners を呼び出す必要があります。
アプリケーションの起動点である main 関数では、ツリーの上位で ChangeNotifierProvider を使用し、作成したモデルインスタンスを提供します。これにより、下位のあらゆるウィジェットから該当の状態にアクセス可能になります。
UI 側での状態参照には、context.watch または Consumer ウィジェットを用います。状態の読み取りのみを行う場合は watch を、状態を変更するメソッドを呼び出す場合は context.read を使用することで、不要な再構築を防ぐことができます。
実装コード例
以下の例では、数値のカウント管理を行う ItemCountModel を定義し、増減操作ができるシンプルなインターフェースを実装しています。状態の変更はモデル内で行われ、UI は自動的に更新されます。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// 状態を保持するモデルクラス
class ItemCountModel with ChangeNotifier {
int _value = 0;
int get value => _value;
void increase() {
_value++;
notifyListeners();
}
void decrease() {
if (_value > 0) {
_value--;
notifyListeners();
}
}
}
void main() {
runApp(
// ツリー全体にモデルを提供
ChangeNotifierProvider(
create: (_) => ItemCountModel(),
child: const MaterialApp(
home: CounterScreen(),
),
),
);
}
class CounterScreen extends StatelessWidget {
const CounterScreen({super.key});
@override
Widget build(BuildContext context) {
// 状態を監視して取得
final model = context.watch<ItemCountModel>();
return Scaffold(
appBar: AppBar(
title: const Text('State Management Demo'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${model.value}',
style: const TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 40),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
// 状態更新メソッドを呼び出し
context.read<ItemCountModel>().decrease();
},
child: const Text('Decrease'),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
context.read<ItemCountModel>().increase();
},
child: const Text('Increase'),
),
],
),
],
),
),
);
}
}
この構造では、increase または decrease が実行され notifyListeners が呼ばれると、context.watch を使用している build メソッドが再実行され、最新の数値が画面に反映されます。ボタン操作による状態変更と、それによる UI 更新のフローが明確に分離されている点が特徴です。