🥲
私のProviderScopeの理解が間違っていた話
tl;dr
ProviderScope
は、"スコープ"を作ります。
おそらく、外部からそのスコープ内に依存したProviderは取得できないと思います。
動作確認用DartPad
https://dartpad.dev/?id=6eecc76dcd7a7b7395425280d1275662
やりたかったこと
ProviderScopeを利用して、特定のIDやtag文字列(キー情報)を注入するWidgetを作成した。
任意のWidgetをそれで包むことで、その内部ではキー情報を取り回す必要がなくなった。
class _TagWrapper extends ConsumerWidget {
const _TagWrapper({
required this.tag,
required this.child,
});
final String tag;
final Widget child;
Widget build(BuildContext context, WidgetRef ref) {
return ProviderScope(
overrides: [
tagProvider.overrideWithValue(tag),
],
child: child,
);
}
}
// 利用時
_TagWrapper(
tag: 'tag1',
child: _Counter(), //<- このWidgetの中では、tagがrefから取得できる。
),
これを利用して、そのキー情報に依存する状態も取得できるようになった。
final counterProvider = StateProvider<int>(
(ref) {
final tag = ref.watch(tagProvider);
// some process uses tag.
return 0;
},
dependencies: [tagProvider],
);
同じようなことが .family
でも実現できるが、 ref.watch(counterProvider(tag))
のように毎回 tag
を指定しなければならなく、冗長に感じたため。
期待した動作
2つの ProviderScope
があるが、同一のtag文字列でoverrideしている。
同一の情報によってProviderが取得されるため、単一のインスタンスが取得できると思った。
(青枠のWidgetでインクリメントした値が、上部の赤枠部分の値にも反映されると期待した)
実際の動作
別々のインスタンスが返却されるため、値の同期はされない。
より具体的な話
上記の例では、1つのWidget内で完結していたが、実際に遭遇したのは以下のケース。
- 1つのページ全体を
ProviderScope
で囲み、「このページで利用するキー情報はこれ」という感じで、その子Widgetではキー情報をかんたんに取得できるようにしていた。- アプリ内でPicture in Pictureのようにページを重ねて表示するケースがあり、「アプリ全体として、現在のキー情報はこれ」という指定ができなかった(これができれば、
StateProvider
的にもたせることもできた)
- アプリ内でPicture in Pictureのようにページを重ねて表示するケースがあり、「アプリ全体として、現在のキー情報はこれ」という指定ができなかった(これができれば、
- ページから、
showModalBottomSheet
を利用して、その表示設定を変更できる機能が存在した。 -
showModalBottomSheet
のbuilder
内は、別のWidget treeとなるため、同じ値でoverrideするProviderScope
で囲むことで、ページ内の情報(ViewModel的なもの)を操作しようとした。 - 結果、
builder
内のWidgetでは、新規のページ情報が取得されてしまい、下のページの情報が更新できなかった。
今回のケースでは、ボトムシート内では単純な参照だけできればよかったので、ページ側の ref
を showModalBottomSheet.builder
の中に渡してしまった。
// 受け渡しイメージ
void _showMySheet() {
showModalBottomSheet(
builder: (BuildContext context) {
return MySheet(
...
pageRef: ref, // <- ADD
),
// 利用イメージ
Widget build(BuildContext context, WidgetRef ref) {
final viewModel = pageRef.read(myPageProvider);
}
あんまり良くない気はしている。。。
Discussion