ScopedProvider<Listened> constructor

ScopedProvider<Listened>(
  1. Listened _create(
    1. ScopedReader watch
    )?, {
  2. 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,
      );