# RiverpodとProvider ## Riverpodとは? RiverpodとはFlutterにおける状態管理をサポートするパッケージです。 ## Riverpodを使うメリット Riverpodでは以下のようなProviderの弱みを解消しています。 ■Providerのデメリット ・ステートの参照許可のないwidgetからアクセスをするとProviderNotFoundExceptionのエラーが発生する ・同じ型のProviderを利用できない など… ■Riverpodのメリット ・ProviderNotFoundExceptionが起こらない! ・同じ型で、複数の Providerを使える! ・Providerをグローバルに利用できる! など ## Riverpod のインストール ■インストール方法 以下を参照しました。 flutter_riverpod: ^1.0.3 https://pub.dev/packages/flutter_riverpod/versions/1.0.3/install Riverpodのパッケージは以下の3種類。 1. flutter_riverpod:FlutterでRiverpodを使うための基本機能が提供される 2. hooks_riverpod:flutter_hooksとRiverpodの両方を使用する想定で作られている 3. riverpod:Dartパッケージ(Dartのみで動くため,Flutterに対応していない) 今回はflutter_riverpodを使用します。 ## Providerの作成 ### 1.providerの中身を作成する。 providers.dartを新規で作成する。 ここには状態管理したい値を定義します。 = Providerを実装します。 ``` ■providers.dart import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; final CounterProvider = ChangeNotifierProvider((ref) => Counter()); class Counter extends ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } } ``` 複数種類のProviderが用意されており、今回はその中の一つである「ChangeNotifierProvider」を使用します。 ChangeNotifierProviderは、状態管理している値に変更があればその情報を参照しているwidgetに通知を飛ばし、UIを再描画してくれます。 ※今回はCounter()クラスをProviderとして保存しています。 > class Counter extends ChangeNotifier {} Counter()クラスは、ユーザーがボタンを押した回数を記録し、_count変数と、ユーザーがボタンを押したときに_count変数に+1を行うincrement()メソッドを実装しています。 ChangeNotifierを継承し、increment()メソッドの中でnotifyListeners()を実行することで値の変更をUIに伝え、再描画をwidgetに促すことができます。 ### 2.Providerを導入する。 main.dartを以下のように書き換える。 ``` ■main.dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:test_for_riverpod/providers.dart'; // 「ProviderScope」で「MyApp」全体を囲む。 void main() { runApp(ProviderScope(child: const MyApp())); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } // 「ConsumerWidget」を使用する。 class MyHomePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // providers.dartをみてくれる人(counterProvider)を作る。 var counterProvider = ref.watch(CounterProvider); return Scaffold( appBar: AppBar( title: Text('Riverpod Demo Home Page'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), Text( // providers.dartをみてくれる人にお願いしてcounterProvider内の「count」を文字列として表示させる。 counterProvider.count.toString(), style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { // providers.dartをみてくれる人にお願いしてcounterProvider内の関数「increment」を実行させる。 counterProvider.increment(); }, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } } ``` ref.watch(<プロバイダー名>)を指定することで、providerを呼び出すことができます。 = providers.dartをみてくれる人を作れます。 ちなみにこの.watch()が非常に優れもので、他のwidgetでCounterProviderの内容が変更されたらその変更を検知して、影響のある他のwidgetにも同様の変更を加え整合性を持つことができます。 ちなみに.watch()だけでなく、.read()を使うとProviderの変更はされず最初に呼び出したProviderの状態を使うことになります。 ## 補足 ConsumerWidgetとは 「値を渡す・受け取る」という動作をさせるためのWidget。 ConsumerWidgetを継承するとbuildメソッドからScopedReaderが受け取れるようになると。こっちの方が手っ取り早い感じがするが、継承したWidget全体が再描画の対象範囲になってしまうので、パフォーマンスをすごく気にする場合はConsumerを使って再描画の対象範囲を制限すると良いかもしれない。 ConsumerWidget https://zenn.dev/umatoma/articles/2026ef43bdb0f4#consumerwidget Consumer https://zenn.dev/umatoma/articles/2026ef43bdb0f4#consumer ## 疑問点 counterProviderを作成するとき、以下のように「var」を使う。 > var counterProvider = ref.watch(CounterProvider); 数値や文字列ではないため「var」を使用するという認識で間違いないのか?? ### あきひろさんからの回答 counterProviderの前につけれるのは↓の2種類あって、 修飾子:final/const/static 型:string/int/boolなど 今回だと、↓の書き方がいいかな final counterProvider = ref.watch(counterProvider); この書き方はfinalとcounterProviderの間にvarが含まれてる varは暗黙的なキャストって言って、型を自動変換してくれるから、楽なんだけど、キャストする処理が発生するのと、読み手がなんの型なのかわかりにくいからできるだけ使用は避けたいかな ただその行が長すぎるとこれもまた読みにくいコードになるから、前の会社ではプリミティブ型は絶対型を明記しようみたいなルールがあったりしたかな プリミティブ型は、int/double/boolとか https://wa3.i-3-i.info/word15876.html ## Github↓ https://github.com/mxmx-wsus/test_for_riverpod ## 参考資料 【5分でわかる】RiverpodとProviderをわかりやすく解説 | Flutter入門 https://kimuralog.com/?p=1930#:~:text=Riverpod%E3%81%AFFlutter%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E7%8A%B6%E6%85%8B,%E7%AE%A1%E7%90%86%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82