【Flutter】riverpod 1.0.0-dev.0の変更点まとめ
今朝、riverpodの1.0.0-dev.0が公開されました!以前記事で触れましたが、4月の半ばごろにriverpodの作者の方からriverpodに破壊的変更が入るかもしれないというアナウンスがありました。
こんにちは、とりかつ((@torikatsu923)https://twitter.com/torikatsu923)です。
今朝、riverpodの1.0.0-dev.0が公開されました!
I have published a dev-release of Riverpod v1:
riverpod 1.0.0-dev.0Go give it a try 🎉https://t.co/OWGQ6evu4O
— Remi Rousselet (@remi_rousselet) 2021年6月20日
以前記事で触れましたが、4月の半ばごろにriverpodの作者の方からriverpodに破壊的変更が入るかもしれないというアナウンスがありました。
【Flutter】今週のインプット 2021/5/17-23 - torikatsu.dev
Changelogを確認したところ、今回のアップデートはそのアナウンスを受けてのものと考えて良さそうです。 個人的に欲しかった機能がつまっているアップデートなので、使うのがとても楽しみです!
今回のアップデートは変更点が20個程含まれており、破壊的変更も多く含まれています。
hooks_riverpod 1.0.0-dev.0 | Flutter Package
そこで今回の記事ではriverpodの変更点についてまとめていきます。
アップデートの方針の考察
今回のアップデートのキーは「Providerへアクセスする方法を統一する」だと思われます。 初めてriverpodを触る時、Providerをreadする方法の複雑さにウッとなる方も多いと思います。
以前の作者さんの(アナウンス)https://github.com/rrousselGit/river_pod/issues/335や、change logでproviderへのアクセス周りで破壊的変更が多く入っていることから、 アップデートにはProviderへアクセスする方法を統一し、よりシンプルにするにする目的があると考えられます。
実際に変更後はref.read, ref.watch, ref.listenによってProviderへのアクセス、UIでの状態の監視、状態変更の監視が可能になっています。
以下ではこの点を踏まえつつ、変更点を追っていきます。
変更点一覧
useProviderの削除
HookWidgetのuseProviderがなくなり、Widget refを使用してProviderへアクセスするように変更が入っています。 HookConsumerWidgetを継承することで、buildの引数にWidget refを受け取ることができるようになります。
Before
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
useState(...);
int count = useProvider(counterProvider);
...
}
}
After
class Example extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
useState(...);
int count = ref.watch(counterProvider);
...
}
}
pub devより引用
ConsumerWidgetのbuildメソッドでWidget refを受け取るようにする変更
従来は以下のようにbuildメソッドでScopedReaderを受け取っていましたが、変更後はWidgetRefを受け取るようになりました。 変更後はProviderの変更をwatchしたい際はref.watchとする必要があります。
Before
class A extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final state = watch(someProvider);
}
}
After
class A extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(someProvider);
}
}
ProviderListenerを非推奨にする変更
WidgetRefにlistenという状態の変更を監視する関数ができました。その関係で、今まで状態の変更を監視する際に使用されていたProviderListenerが非推奨になりました。
この変更により、今までWidgetをネストしなければならなかったところをとてもシンプルに記述することができるようになりました!
Before
class A extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderListener(
provider: someProvider,
onChange: (context, state, child) { ... },
child: Container( ... ),
);
}
}
After
class A extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(someProvider, (state) {
// something
}
}
}
ConsumerStatefulWidget, ConsumerStateの追加
StatefulWidgetとConsumerWidgetが合体したConsumerStatefulWidgetが追加されました。 StatefulWidgetのライフサイクルにフックしてProviderの処理を走らせたいが、状態の管理はProviderに行わせたいみたいな場合、いままではStatefulWidgetでWidgetを定義して、build内でConsumerで括ってあげる必要がありました。 今回のConsumerStatefulWidgetの追加で、Consumerで括る必要がなくなったため、よりシンプルに記述できるようになりました。
watchでselectでstateの特定の値の変更のみ検知できるようにする変更
userのフィールドにusernameとstatusがあるとします。 以下のコードではstatusに変更があった場合はリビルドされず、usernameに変更があった場合にリビルドされるようになりました。
final userProvider = StateNotifierProvider<UserController, User>(...);
Consumer(
builder: (context, ref, _) {
bool userName = ref.watch(userProvider.select((user) => user.name));
},
)
グローバルな状態とリビルドについて悩んでいたため、この変更は個人的にとても嬉しいです!
overrideWithValueの削除
今まで.familyで作成したProviderはoverrideする必要がありました。その際値のみをオーバーライドするoverrideWithValueとProviderごとオーバーライドするoverrideWithProviderの2種類の方法がありました。 しかし、今回の変更でoverrideWithProviderに一本化されたようです。
ProviderObserverが変更前と変更後の2種類の状態を受け取れるようにする変更
ProviderObserverが変更の前後の状態を受け取れるように変更が入りました。 これによって状態の差分をみたりする処理が書きやすくなりました。
ProviderObserver.mayHaveChangedの削除
ProviderObserver.mayHaveChangedが削除されました。 これは破壊的変更ですが、個人的にはあまり使っていない機能だったため影響はありませんでした。
ref.listenの追加
先で触れたようにref.listenが追加されました。 バージョンアップ後はダイアログの表示等はこれを使用することになります。
ProviderReferenceを非推奨にする変更
ProviderReferenceが非推奨になり、代わりにProviderBaseが採用されました
全てのProviderがProviderRefBaseをパラメータとして受け取るようにする変更
今までProviderは以下のようにrefを受け取っていましたが、これがProviderRefBaseに変更されました。
Provider((ref) => ...);
この変更でrefがstateというプロパティを持つようになり、ref.state++のようにすることで状態を変更することができるようになりました。 また、StateProviderのrefにはcontrollerというプロパティが追加され、ref.controller....でStateControllerにアクセスできるようになりました。
ProviderReference.mountedの削除
ProviderReference.mountedが削除されました。代わりにonDisposeを使用することで似たような挙動を実現できるようです。
Provider<T>((ref) {
var mounted = true;
ref.onDispose(() => mounted = false);
});
ProviderContainerのdebugproviderValuesとdebugProviderElementsの削除
ProviderContainerのdebugProviderValuesとdebugProviderElementsが削除され、 ProviderContainer.getAllProviderElementsを使用できるようになりました
StreamProviderのlast, stream, futureがプロバイダーの再構築の回数に依存しないようにする変更
関連するProviderの再構築の回数に関係ないfuture, streamを外部に公開するようになりました。
Providerの値が同じ時、Providerをオーバーライドできるようにする変更
StreamProvider<User>があった時、Provider<AsyncValue<User>>でオーバーライドすることができるようになりました。
ref.refreshの呼び方変更
ref.refreshの呼び方がref.container.refreshに変更されました。
ref.onDisposeのタイミング変更
Providerの依存関係の変更がわかったらすぐにonDisposeが呼ばれるようになりました。
Providerの状態の再計算のタイミング変更
Providerの依存関係が変更されてリスナがいるとき、次の読み取りまで状態の再計算を待たなくなりました。
ProviderContainer.pumpの追加
ProviderContainer.pumを使うことで、awaitのように簡単にproviderが状態の変更を通知するまで待つことができるようになりました。
familyとdisposeを併用した時に状態が残る問題の修正
familyとdisposeを一緒に使用した際状態が残ってしまう問題がありましたが、今回のアップデートで解決されたようです。
おわりに
今回の記事ではriverpodの変更点を追っていきました。 Providerへのアクセスする際の構文がrefを経由して行われるように統一されたので、より使いやすくなったと感じています! またいくつかの変更によってシンプルに処理をかけるようになったのもとても嬉しいです!
それではよい開発ライフを!
-––