9

I'm working on a simple demo where the user logs in, sees a list of items, clicks on an item, and then sees its details. These are 3 screens but let's say two flows where one deals with login and the other with items.

I'm managing my state using provider package. I placed a LoginRepository provider on my root widget because that state is needed everywhere. This works great.

Now I need an ItemRepository which should be available on the ItemListScreen and the ItemDetailScreen. To implement that scoping, I decided to create a common ancestor widget called ItemScreens. This is where I placed my provider and my nested Navigator.

I navigate my screens using Navigator.of(context) to make sure the closes ancestor navigator is used. I fetch my provider using Provider.of<ItemRepository>(context) to make sure the closes ancestor state is used.

This apparently does not work because I get a Could not find the correct provider. I understand that the provider from the sibling route cannot be accessed by another sibling but here I'm nesting navigators I would expect the ItemRepository to be placed at ItemScreens which then handles subrouting so that these two routes can fetch providers from the parent route?

Sidenote:
Flutter documentation clearly states that providers should be lifted to achieve proper scoping. But I've never seen an example where the provider was lifted to a non-root widget. If all we can do is place providers into the root widget, that should be clearly stated.

EDIT: Re-creating providers on several widgets is kind of an alternative but that's not really scoping it's just pseudo passing by value.

EDIT: Putting all providers on the root widget with MultiProvider isn't a good idea because it creates a global state anti-pattern. Some states should also simply not be created unless needed i.e. the user is visiting that flow/screen.

Spidey
  • 894
  • 1
  • 13
  • 29
  • Have you found a good solution to this problem? Currently I'm using GetIt + Provider to achieve scoping but this solution feels fragile. – masterwok Jun 22 '22 at 15:52
  • 1
    No, this question came up during a hackathon. I didn't revisit the topic since then. – Spidey Jun 26 '22 at 22:14
  • 1
    aparently there's no good way to do this in Provider and was one of the main reasons for Riverpod. https://github.com/flutter/flutter/issues/30062#issuecomment-1163290913 – masterwok Jun 27 '22 at 14:12

1 Answers1

0

You can wrap your MaterialApp Widget in main.dart with Multiprovider and give the list of providers you are using, That way App knows which class to treat as providers.

MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (_) => FirstProvider(),
          ),
          ChangeNotifierProvider(
            create: (_) => SecondProvider(),
          ),
        ],
        child: MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Auro',
          home: _decideMainPage(context),
          // routes: routes,
          theme: AllCoustomTheme.getThemeData(),
        ),
      )
  • 6
    Hi, Abhinav welcome to the community and thanks for the answer. I'd like to avoid placing all of my states on the root widget because that would be a global state which isn't desirable. I also wouldn't want to create `ItemRepository` unless I was logged in. For instance, the `ItemRepository` constructor may need an authentication header before it is created. – Spidey Jun 16 '21 at 12:17