4. Riverpod の導入
4.1 Riverpodのインストール
まずはflutter_riverpod をプロジェクトに追加します。
flutter pub add flutter_riverpodflutter pub add flutter_riverpod4.2 Provider の作成
まず、盤面の状態を管理するためのProviderを作成します。
libディレクトリ配下にproviderディレクトリを、さらにその配下にtic_tac_toe_provider.dartを作成します。
🗂 lib
└ 🗂 model : 各種データモデル
- 📄 tic_tac_toe.dart : 三目並べのデータモデル
└ 🗂 provider : 各種プロバイダー
- 📄 tic_tac_toe_provider.dart : 三目並べのプロバイダー
└ 🗂 view : 各種ページビュー
- 📄 board.dart : 三目並べのページビュー
- 📄 main.dart : アプリケーションのエントリーファイル
- 📄 pubspec.yaml : アプリケーションで使う依存関係の設定🗂 lib
└ 🗂 model : 各種データモデル
- 📄 tic_tac_toe.dart : 三目並べのデータモデル
└ 🗂 provider : 各種プロバイダー
- 📄 tic_tac_toe_provider.dart : 三目並べのプロバイダー
└ 🗂 view : 各種ページビュー
- 📄 board.dart : 三目並べのページビュー
- 📄 main.dart : アプリケーションのエントリーファイル
- 📄 pubspec.yaml : アプリケーションで使う依存関係の設定tic_tac_toe_provider.dart作成手順は下記の通り進めましょう。
- プロジェクトの
libディレクトリの中に、新しくproviderというディレクトリを作成します。 - その中に
tic_tac_toe_provider.dartというファイルを作成します。 - 以下のコードをファイルに書き込みます。
// lib/provider/tic_tac_toe_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tic_tac_toe_handson/model/tic_tac_toe.dart';
final ticTacToeProvider = StateProvider<TicTacToe>((ref) {
return TicTacToe.start();
});// lib/provider/tic_tac_toe_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tic_tac_toe_handson/model/tic_tac_toe.dart';
final ticTacToeProvider = StateProvider<TicTacToe>((ref) {
return TicTacToe.start();
});StateProviderを使うことでProviderで保持する盤面の状態を変更することができます。初期状態はTicTacToe.start()で設定しておきます。
ちなみに、Providerに.autoDispose修飾子をつけると、そのProviderがアプリケーションのどこからも使われなくなった際にRiverpod側で自動でデータを破棄してくれます。特に理由がない限りは.autoDisposeをつけておくことをお勧めします。ちなみに、AutoDisposeStateProviderというProviderを使っても同様のことを実現できます。
StateProvider.autoDisposeStateProvider.autoDispose4.3 状態変化に応じて UI を更新する
次にUIに関するコードを変更していきます。
4.3.1 main.dartの更新
Riverpodをアプリケーション内で利用するには、ProviderScopeでラップする必要があります。 Widget内でProviderの値を読み取れるようにしておくためには、アプリケーション全体をProviderScopeでラップする必要があります。
// lib/main.dart
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}// lib/main.dart
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}4.3.2 view/board.dartの更新
Widget内でproviderにアクセスするにはConsumerWidgetを継承します。
ConsumerWidgetでは、buildメソッドの第二引数にWidgetRef refを追加されます。これを使ってProviderにアクセスすることができ、ref.watchでproviderで保持している状態の値を読み取ることができます。また、provider内の状態が変わるたびにConsumerWidgetが再描画されるようになります。
// lib/view/board.dart
class Board extends ConsumerWidget {
const Board({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final ticTacToe = ref.watch(ticTacToeProvider);
// ...略...
}
}// lib/view/board.dart
class Board extends ConsumerWidget {
const Board({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final ticTacToe = ref.watch(ticTacToeProvider);
// ...略...
}
}refにはref.watchとref.readの2種類の主な使い方があります。ref.watchはproviderの状態が変わるたびに再描画が走りますが、ref.readはその時の値を一度だけ読み取るだけです。
4.3.3 状態の更新方法
最後に状態の更新処理を変更していきます。ref.read(ticTacToeProvider.notifier).stateでproviderの状態にアクセスし、それに対して値を代入することで状態を更新しています。
マークを配置する処理は以下のようになります。
return GestureDetector(
onTap: () {
final winner = ticTacToe.getWinner();
if (mark.isEmpty && winner.isEmpty) {
ref.read(ticTacToeProvider.notifier).state = ticTacToe.placeMark(row, col);
}
},
// ...略...
)return GestureDetector(
onTap: () {
final winner = ticTacToe.getWinner();
if (mark.isEmpty && winner.isEmpty) {
ref.read(ticTacToeProvider.notifier).state = ticTacToe.placeMark(row, col);
}
},
// ...略...
)また、ゲームのリセットは以下のようになります。
ElevatedButton(
onPressed: () {
ref.read(ticTacToeProvider.notifier).state = ticTacToe.resetBoard();
},
child: const Text('ゲームをリセット'),
)ElevatedButton(
onPressed: () {
ref.read(ticTacToeProvider.notifier).state = ticTacToe.resetBoard();
},
child: const Text('ゲームをリセット'),
)これで状態の管理と盤面のUI更新をRiverpodを使って行うことができるようになりました。
コントリビューター
