Flutterには、依存性注入を行うための機能が多数提供されており、それらの中には、Providerパッケージも含まれています。 Providerは、Flutterにおける依存性注入のためのパッケージで、簡単にデータを渡すことができます。しかし、大規模なアプリケーションを開発する場合、Providerのデータ管理が複雑になります。そこで、Riverpodというパッケージが登場しました。
Riverpodは、Flutterにおける依存性注入をより強力に行えるようにするパッケージです。Riverpodは、Providerパッケージをベースにしており、より強力なスコープ管理機能や、簡単に切り替えられるテストモードなど、多数の機能を提供しています。ここでは、Riverpodの詳細な使い方を紹介します。
Riverpodの導入
Riverpodを使用するには、flutter_riverpod
パッケージを追加する必要があります。pubspec.yamlファイルに以下の依存関係を追加してください。
dependencies:
flutter_riverpod: ^2.3.6
Riverpodを使用するには、FlutterのウィジェットツリーのルートであるMaterialApp
ウィジェット内で、ProviderScope
を使用してRiverpodを初期化する必要があります。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: MyHomePage(),
);
}
}
Riverpodの使い方
Riverpodでは、Provider
と呼ばれる機能を使用して、データを提供します。Provider
は、生成された値を格納し、必要に応じてリビルドをトリガーする値の変更を検出します。ここでは、簡単なカウンターアプリを作成し、Riverpodの基本的な使い方を説明します。
final counterProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});
class Counter extends StateNotifier<int> {
Counter() : super(0);
void increment() => state++;
}
class MyHomePage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Count:',
),
Text(
'$count',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
この例では、`counterProvider`という`StateNotifierProvider`を作成して、初期値として`0`を設定しています。`MyHomePage`ウィジェットは、`ConsumerWidget`を継承しており、`counterProvider`を監視して、値が変更された場合にウィジェットを再ビルドします。`ref.read()`メソッドを使用して、`counterProvider`を更新し、カウンターを増やすことができます。
複数のProviderScopeを管理する
Riverpodでは、`ProviderScope`を使用して、異なるスコープでデータを管理することができます。例えば、アプリのグローバルな状態を管理する`ProviderScope`と、画面単位で状態を管理する`ProviderScope`を作成することができます。`ProviderScope`により、データを分離することができます。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: MyHomePage(),
);
}
}
class Counter extends StateNotifier<int> {
Counter() : super(0);
void increment() => state++;
}
final localcounterProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});
final globalCounterProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});
class GlobalCounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(globalCounterProvider);
return Text(
'$count',
style: Theme.of(context).textTheme.headlineLarge,
);
}
}
class LocalCounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(localcounterProvider);
return Text(
'$count',
style: Theme.of(context).textTheme.headlineMedium,
);
}
}
class MyHomePage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(localcounterProvider);
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Global Count:',
),
GlobalCounterWidget(),
SizedBox(height: 20),
ProviderScope(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Local Count:',
),
LocalCounterWidget(),
],
),
overrides: [
localcounterProvider.overrideWith((ref) => Counter())
],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(localcounterProvider.notifier).increment();
ref.read(globalCounterProvider.notifier).increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
この例では、globalCounterProvider
というグローバルなStateNotifierProvider
を作成し、グローバルな状態を管理します。GlobalCounterWidget
ウィジェットでは、globalCounterProvider
を監視して、その値を表示しています。
ProviderScope
を使用して、ローカルな状態を管理するために、localcounterProvider
を定義し、LocalCounterWidget
ウィジェットで監視します。ProviderScope
内でlocalcounterProvider
を再定義し、初期値を0
に設定しています。これにより、グローバルなカウンターとローカルなカウンターが独立して存在することができます。
MyHomePage
ウィジェットは、2つのカウンターウィジェットを含んでいます。1つはグローバルなカウンターを監視し、もう1つはローカルなカウンターを監視します。ProviderScope
を使用して、ローカルなカウンターウィジェットにcounterProvider
を渡し、グローバルなカウンターとは独立してカウントアップすることができます。
上記サンプルコードの場合、localcounterProvider
はoverride
部分でCounter()を指定しています。そのため、ボタンを押下したら、globalは値がカウントアップしますが、localの値はカウントアップせずにゼロのままになります。
まとめ
Providerパターンの改良版であり、型安全性が高く、可読性の向上を提供します。また、テストを容易にするためにProviderScopeを使用して異なる状態を提供できます。Riverpodは、DI(依存性注入)やルーティングとの統合も容易で、アプリケーション全体の開発効率を向上させます。状態管理において柔軟性が求められる場合において、Riverpodは強力な選択肢となります。
Riverpod以外の状態管理方法については下記記事および、その記事内のリンクページの詳細な使い方を参照してみて下さい。