Provider の Consumer と Riverpod の Consumer は別モノなので2021/01時点のコードを追いかけてみた
個人的に混同してしまっていたのでメモ
TL;DR
- Provider の Consumer と Riverpod の Consumer は別モノ
- Provider >
Consumer<T>
- provider package > documentation > Consumer<T> class | pub.dev
- provider/lib/src/consumer.dart | GitHub
- Riverpod >
Consumer
- flutter_riverpod package > documentation > Consumer class| pub.dev
- river_pod/packages/flutter_riverpod/lib/src/consumer.dart | GitHub
Provider の Consumer<T> と Riverpod の Consumer は別モノだった
勝手に勘違いしてただけなのですが
Provider の Consumer<T>
と Riverpod の Consumer
は別モノでした。
Provider から Riverpod への乗り換え解説系の記事を色々と読んでいて
hooks_riverpod を使わず flutter_riverpod を使う場合は
Consumer.builder
の watch
で Provider の変更を監視する、という方法を
なんの疑いもなく Provider の Consumer<T>
と同様の物だという先入観で考えて
理解に苦しんでいたのですが、
APIリファレンスをみたところ全くの別モノだったので「あぁ、まぁそりゃそうか」となりました。
せっかくなのでコードの方も追いかけてみましょう。
Provider の Consumer<T>
...
class Consumer<T> extends SingleChildStatelessWidget {
/// {@template provider.consumer.constructor}
/// Consumes a [Provider<T>]
/// {@endtemplate}
Consumer({
Key key,
this.builder,
Widget child,
}) : assert(builder != null),
super(key: key, child: child);
/// {@template provider.consumer.builder}
/// Build a widget tree based on the value from a [Provider<T>].
///
/// Must not be `null`.
/// {@endtemplate}
final Widget Function(BuildContext context, T value, Widget child) builder;
Widget buildWithChild(BuildContext context, Widget child) {
return builder(
context,
Provider.of<T>(context),
child,
);
}
}
...
nested パッケージの SingleChildStatelessWidget
を継承したクラスですね。
キモは Provider.of<T>(context)
の部分で、
このおかげで値の変更に応じて処理されるんですね。
- nested | pub.dev
- nested package > documentation > nested > SingleChildStatelessWidget abstract class | pub.dev
Riverpod の Consumer
...
typedef ConsumerBuilder = Widget Function(
BuildContext context,
ScopedReader watch,
Widget child,
);
...
class Consumer extends ConsumerWidget {
/// {@template riverpod.consumer}
const Consumer({
Key key,
ConsumerBuilder builder,
Widget child,
}) : _child = child,
_builder = builder,
assert(builder != null, 'the parameter builder cannot be null'),
super(key: key);
final ConsumerBuilder _builder;
final Widget _child;
Widget build(BuildContext context, ScopedReader watch) {
return _builder(context, watch, _child);
}
}
...
abstract class ConsumerWidget extends StatefulWidget {
/// {@macro riverpod.consumerwidget}
const ConsumerWidget({Key key}) : super(key: key);
Widget build(BuildContext context, ScopedReader watch);
_ConsumerState createState() => _ConsumerState();
}
...
なんと! Riverpod の Consumer
は StatefulWidget
を継承した ConsumerWidget
を継承したものでした。この時点で全くチガウモノですね。
ScopedReader
の定義はこちらです
river_pod/packages/riverpod/lib/src/framework/scoped_provider.dart | GitHub
Riverpod の公式サンプルでは Consumer
を使わずに
ConsumerWidget
をそのまま使っているサンプルも上がっていますし、
hooks_riverpod の方でも HooksWidget
を使うサンプルが推奨されている(?)ようですし
ConsumerWidget
を直接使って欲しいのかな……? とも考えられました。考えすぎかもしれませんが。
まとめ
というわけで皆さんは混同しないように注意してください。
といっても、混同してしまっていたのは私だけかもしれませんね。
参考
- Flutter package:provider の各プロバイダの詳細 | Qiita
- Single Child な Widget を使いこなす. 🧒 ← Single Child | by mono | Flutter 🇯🇵 | Medium
Discussion