初めに
Providerの作者が去年riverpodという状態管理法を作成しました。
今回は、私自身が使っている状態管理法freezed + stateNotifier + Provider のProviderの部分をRiverpodに変更して状態管理していこうと思います。
Riverpodとは
Riverpodとは簡単に言うと、Providerの上位互換です。
最近、Flutterの公式ドキュメントにも状態管理の手法としてRiverpodが追加されたみたいです。
Riverpodの公式ホームページは以下です。
Riverpodホームページ
英語ですが、非常にわかりやすいので参考にしてみてください。
何がProviderの上位互換なのかというと、
- MultiPleProviderのようにWidgetをProviderでラップする必要がなくなった。
- グローバル変数でProviderを使用可能。
- BuildContext配下で使う必要がなくなった。
etc...
いろいろあります。
雄一の欠点といえば、
- これから破壊的な変更がある可能性あり。
- 公式ドキュメントがあるが、日本語の説明がほぼない
- Hooks的な使い方が出来るのですが、公式がHooksをいまだ認めていない(Hooksなしでも使用可能。)。
riverpodの設定
riverpodを使用する際、3つパターンがあるみたいです。
- Flutterのみ使用場合 → flutter_riverpodパッケージ
- Dartのみ使用 → riverpodパッケージ
- Flutter とflutter_hooksの場合 → flutter_hooks、hooks_riverpodパッケージ
Flutter使用者は、1 or 3を主に使用していきます。
riverpod開発者は3を主に使用しています。
今回は、1の場合、つまりhooksは使わない場合で行っていきます。
Riverpodの使い方
Providerの設定
providerでは上の方でwidgetをラップしていましたが、riverpodの場合はどうやってProviderを設定するのでしょうか、下のコードを見てみましょう。
//グローバル変数でProviderを設定。
final newsProvider = StateNotifierProvider((ref) => NewsController());
@freezed
abstract class NewsState with _$NewsState {
factory NewsState({
bool loading,
List<Article> articles,
}) = _NewsState;
}
class NewsController extends StateNotifier<NewsState> {
NewsQueryService _newsQueryService;
NewsController(this._newsQueryService) : super(NewsState(loading: false));
Future<void> getNews() async {
List<Article> articles = [];
articles = await _newsQueryService.getNews();
state = state.copyWith(articles: articles);
}
}
以前は、ProviderをWidgetのなるべく上にラップして置いていましたが、riverpodではその必要がなくなり、
final newsProvider = StateNotifierProvider((ref) => NewsController());
と書くだけでProviderの設定になります。(今回はStateNotifierを使用するため、StateNotifierProvider使用。)
Providerの読み方。
Providerの設定が出来たので、widget内でProviderを読んでみましょう。
以前は、
Provider.of(context, listen :false).getNews();
で、ロジックやstateを読み込んでいました。
Riverpodではどうなのか下のコードを見てみましょう。
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: (){
context.read(newsProvider).getNews();
},
),
appBar: AppBar(
centerTitle: true,
title: Text("Demo"),
),
body: HeadPage()
);
}
}
class HeadPage extends StatelessWidget{
@override
Widget build(BuildContext context) {
//controllerのメソッドを読む場合は、context.read(Provider名).メソッド名と書いて呼び出す。
final _fetch = context.watch(newsProvider).getNews();
// TODO: implement build
return FutureBuilder<void>(
future: _fetch,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
print("エラーが出たよ");
}
return HeadNewsListPage();
},
);
}
}
//viewの部分表示
class HeadNewsListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 10),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 10),
//Hooksを使わない場合はConsumerでProviderを使用する。
child: Consumer(
builder:(context, watch, child){
//stateを呼び出す場合は、watchで監視し(変更を監視)、下のように書くと、NwesState内のstateを取得可能。
final state = watch(newsProvider.state);
return ListView.builder(
itemCount: state.articles.length,
itemBuilder: (context, index) {
return ArticleTile(state.articles[index]);
},
);
}
),
),
);
}
}
}
このように、providerを読みだし、widget内で使うことが出来ます。
また、hoksを使用した書き方は、useProviderを使ってよビアスことが出来ます。
ちなみにこのuseProviderはonPressed内では使えないので、上記のcontext.readを使用しましょう。
終わりに
riverpod使ってみた感想は、非常に使いやすいです。
こんご、自分の開発は積極的にriverpodを使用していこうと思うので、hooksを使用した場合などまた記事にできればと思います。
ちなみに、NwesControllerは引数で、NwesQueryServiceもってきているのですが、引数でのもってきかたが下のコードになります。
final newsQueryService = Provider((ref) => NewsQueryService());
final newsProvider = StateNotifierProvider((ref) => NewsController(ref.watch(newsQueryService)));
参考になればと思います!!