ScopedProvider<Listened> constructor
- Listened _create(
- ScopedReader watch
- String? name,
A provider that may behave differently for a specific part of the application.
A common use-case for ScopedProvider is to avoid having to pass the argument of a Provider.family in the widget tree.
More specifically, we may have a ListView
that wants to render of list of
products. But for both performance and simplicity, we do not want to pass
the product id/index to the item's constructor.
In that situation, we could use ScopedProvider, in combination with ProviderScope
,
to allow our items to access the id/index, without receiving it from its
constructor.
For this, we first need to define our ScopedProvider like any other provider:
final currentProductIndex = ScopedProvider<int>((_) => throw UnimplementedError());
Note:
We made our ScopedProvider throw by default, as our list items by design
requires an index to be specified.
Another possibility would be to return null
and have the item handle the
null
scenario.
Then, inside our ListView
, we will use ProviderScope
to override the
value exposed by ScopedProvider:
ListView.builder(
itemBuilder: (context, index) {
return ProviderScope(
overrides: [
currentProductIndex.overrideWithValue(index),
],
child: const ProductItem(),
);
}
)
This code means that for the first item in our ListView
, currentProductIndex
will return 0
; whereas for the second item, it will return 1
, ...
Finally, we can read the item index inside our ProductItem
:
class ProductItem extends ConsumerWidget {
const ProductItem({Key? key}): super(key: key);
@override
Widget build(BuildContext context, ScopedReader watch) {
final index = watch(currentProductIndex);
// do something with the index
}
}
Now, our ProductItem
was able to obtain the product index it is associated with.
The interesting aspect of such code is that ProductItem
was
instantiated using the const
keyword, but still renders dynamic content.
What this means is, even if ListView
rebuilds, our ProductItem
will
not rebuild unless what it uses changed.
ScopedProvider with no default behavior
A common use-case with ScopedProvider is to not provide a default behavior,
and instead always override the provider inside a ProviderScope
.
In this situation, what we can do is pass null
instead of a function
to ScopedProvider:
final example = ScopedProvider<int>(null);
This is equivalent to:
final example = ScopedProvider<int>((watch) => throw UnsupportedError('<some error message>'));
Implementation
ScopedProvider(
this._create, {
String? name,
}) : super(
name,
);