##背景
はじめまして、yamoriです。
状態管理としてこれまでProviderを使用していましたが、とあるプロジェクトでRiverpodを使う機会がありました。
はじめはとっつきにくい印象があったのですが、実際に使ってみると非常にスムーズに移行できた(とはいえ、もっと深めれば違いを感じたりすることもありそう)ので、Providerとの違いについてまとめようと思います。
##対象者
これまでにProvider(主にChangeNotifierProvider)を利用した経験があって、これからRiverpodを使ってみたいという人向けの入門記事です。
##Providerでのカウントアプリ
まず、Flutterの初期アプリであるカウントアプリにProviderを導入してみましょう。
(適切なユースケースではないような気がしますが、分かりやすさ重視のためです。)
providerのパッケージは以下から導入してください。
コードは以下の通りです。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:riverpod_sample/provider_view_model.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//①ChangeNotifierProviderを使用している(ここの書き方少し面倒臭い)
return ChangeNotifierProvider<CountViewModel>(
//②初期化処理がある場合(たとえばinit()など)はCountViewModel..init()とする。
create: (_) => CountViewModel(),
child: Consumer<CountViewModel>(
//③第二引数のmodelを使って、ViewModelの値や関数を参照する
builder: (context, model, child) {
return Scaffold(
appBar: AppBar(
title: Text('${model.title}'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${model.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: model.incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
})
);
}
}
import 'package:flutter/material.dart';
class CountViewModel extends ChangeNotifier {
final String title = 'provider';
int _counter = 0;
get counter => _counter;
void incrementCounter() {
_counter++;
notifyListeners();
}
}
##Riverpodでのカウントアプリ
それでは、上記カウントアプリをRiverpodで実装してみましょう。
Riverpodのパッケージは以下から導入してください。
コードは以下の通りです。
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_sample/count_view_model.dart';
void main() {
runApp(
//①MyAppをProviderScopeで囲む
const ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
//②StateleeWidgetをHookConsumerWidgetに変更
class MyHomePage extends HookConsumerWidget {
MyHomePage({Key? key}) : super(key: key);
@override
//③第2引数にWidgetRef refを追加
Widget build(BuildContext context, WidgetRef ref) {
//④countViewModelを参照することを定義、vmでViewModelを参照する
final vm = ref.watch(countViewModel);
return Scaffold(
appBar: AppBar(
title: Text('${vm.title}'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${vm.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: vm.incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
//①Riverpodのみ設定
final countViewModel = ChangeNotifierProvider((_) => CountViewModel());
//CountViewModelの中身は一緒
class CountViewModel extends ChangeNotifier {
final String title = 'provider';
int _counter = 0;
get counter => _counter;
void incrementCounter() {
_counter++;
notifyListeners();
}
}
ビルドすると、同じカウントアプリができていることがわかると思います。
riverpodのコードはこちらからご覧いただけます。
##おわりに
ViewModelの書き方はほとんど変わらず、Viewの書き方はriverpodの方が簡素だと思います。(初心者の方だと、ProviderのChangeNotifierProvoiderの以下の部分の書き方で(や{の数で苦しんだことがあるかもしれません。
Widget build(BuildContext context) {
//①ChangeNotifierProviderを使用している(ここの書き方少し面倒臭い)
return ChangeNotifierProvider<CountViewModel>(
//②初期化処理がある場合(たとえばinit()など)はCountViewModel..init()とする。
create: (_) => CountViewModel(),
child: Consumer<CountViewModel>(
//③第二引数のmodelを使って、ViewModelの値や関数を参照する
builder: (context, model, child) {
return Scaffold(
appBar: AppBar(
このあたり、Riverpodだと定数を定義するだけなので簡単かなと思います。
どちらを採用すべき等の議論はあると思いますが、まずは使い方を覚えることで、公式ドキュメントを読んでいて引っかかるところも増えると思うので、この記事を書きました。
素敵な年末をお過ごしください!