3

I'm starting to use riverpod and I'm trying to migrate my existing code which was using provider.

With provider, the Providers were scoped in the widget tree. Only the children of the Provider widget could access its model.

It says in the riverpod's doc:

Allows easily accessing that state in multiple locations. Providers are a complete replacement for patterns like Singletons, Service Locators, Dependency Injection or InheritedWidgets.

* "Providers" here refers to the Provider class of the package riverpod.

And the provider package was a simplification/wrapper/API around InheritedWidgets so I guess what was possible with provider is also possible with riverpod.

But I cannot manage to find out how to do it.


Here is a small example of what I am trying to migrate.

class Counter extends ValueNotifier<int> {
  Counter(): super(0);
}


class MyWidget extends StatelessWidget {
  const MyWidget();
  
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Counter>(
      create: (_) => Counter();
      child: Builder(
        builder: (context) {
          return Row(
            children: [
              IconButton(
                onPressed: () {
                  context.read<Counter>().value++;
                }, 
                icon: Icon(Icons.add),
              ),
              Text('Count: ${context.watch<Counter>().value}'),
            ],
          );
        },
      ),
    );
  }
}

Wherever there is a MyWidget widget, the sub-widget has access to a unique/scoped model Counter.

Valentin Vignal
  • 6,151
  • 2
  • 33
  • 73

2 Answers2

6

Scoping providers is done through ProviderScope

You can do:

final provider = ChangeNotifierProvider<Counter>((ref) => throw UnimplementedError());


// in some widget:

return ProviderScope(
  overrides: [
    provider.overrideWithProvider(
      ChangeNotifierProvider((ref) => Counter());
    ),
  ],
)

Although if you can avoid scoping, do so. Scoping providers is generally not recommended and should be avoided as that's fairly advanced.

If you're using a scoped provider so that the state is destroyed when the user leaves the page, a much simpler solution is to use autoDispose:

final provider = ChangeNotifierProvider.autoDispose<Counter>((ref) => Counter());

// No longer needed to scope the provider
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Thanks for the answer, I will try it out. The reason I want to scope the providers is that I might have several times the same pages in my history that need a different instance of the `ChangeNotifier`. For example, I could have several pages of `UserDetails` about different users, so I cannot share the same instance of `ChangeNotifier`. But the pages are still there in the history. That's why I don't think simply using ` autoDispose` will make it work – Valentin Vignal Feb 27 '22 at 04:16
  • 3
    Maybe you want `family` then. – Rémi Rousselet Feb 27 '22 at 12:26
  • Yes, I think that would work for the `UserDetails` example. Now if there is no id, would this still work? For example, a page that could be `UsersList` and has no parameter. The provider could contain filters apply by the user after the page is built. And you could have several instances of `UsersList` in the history that should not share the same provider. – Valentin Vignal Feb 27 '22 at 13:11
  • You could make an enum corresponding to the page that is trying to read the list. – Rémi Rousselet Feb 27 '22 at 13:53
  • I don't think that would work, the page is really the same one. It happens when the users goes twice to the same page and therefore, the same page is twice there in the history. – Valentin Vignal Feb 28 '22 at 01:08
  • Quick Q here, in the case of having a Provider used on 2 pages, one implements the AutomaticKeepAlive mixin then you navigate(push) to the second one which doesn't. In this case, is the auto dispose triggered? Or should provider scope be used here? – Abdelghani Bekka May 07 '22 at 19:45
0

Inside ProviderScope is an InheritedWidget, so you can you multiple ProviderScope just like Provider. Please checkout the code of Remi in this ticket link.

Or you can use .family and .autoDispose for your purpose.

dante
  • 984
  • 3
  • 10
  • 24