This is more about a theoretical discussion of flutter riverpod state management package. Currently I am trying to migrate one kinda huge project from Provider to Riverpod and I encountered an issue which was easily solved in Provider, but seems to be a "show stopper" for Riverpod.
So the issue is following.
Imagine that you are having a ClientDetailScreen that can be opened from multiple places of the app. The basic path to get there is ClientsTab > Search (click on searched name) > ClientDetailScreen opens. However you can also get there from a completely different tab, let’s say a dashboard, where you have messages from the clients and when you click on their name, you get into the ClientDetailScreen as well. Significance of this is that there can be N of ClientDetailScreens open.
From the ClientDetailScreen you can access all kinds of subscreens that are related to this particular client with which you entered the screen. Now the issue arise.
Alright, so in order to access some information about this client across all possible subscreens with Provider package you created a ChangeNotifier on top level of ClientDetailScreen and that means every single screen had its own provider accessible. Meaning that in all subscreens and related widgets you could do:
context.read<ClientProvider>();
and you would access correct data everytime.
With Riverpod, this problem does not seem to be so easily solvable. Riverpod is way more strict about creating providers. Basically they are created as soon as they are used on a global level. I know about family constructor to differentiate between correct instance, however I am hesitant to choose that path especially when it would mean I would have to somehow distribute the clientId across all the subwidgets and that just does not seem right. I am more inclined to the fact that there is something fundamentally wrong with how the app is written. I was trying to solve it via doing a local ProviderScope that would encapsulate whole ClientDetailScreen hoping that all subwidgets would then access correct provider. However for some reason, all the subscreens are pushed on the same level as ClientDetailScreen meaning that they don’t access the correct scope and because of some dependencies I am unable to place the ProviderScope even higher.
Have you ever solved similar problem? Thanks for any response.
2
Answers
In the end I managed to solve this by overriding provider in the ProviderContainer() itself as soon as I have clientData loaded. Then in the subwidgets I am acessing the data not via ref.read, but directly on the container which I expose via simple Provider. I am posting the solution in order to close this thread and maybe for a future reference for somebody in similar need.
Basically I prepare the container like this
On the clientDetailScreen where we load the clientData I override it:
Finally in the subscreens/widgets we can read it like this. Exclamation mark should be ofc in ideal case avoided as much as we can. In this case I know we have the data overriden.
I know it is not ideal solution, however at the current state of the project, I think it is the only viable one without the need to refactor 50+ files and the whole loading logic.
Two solutions. Easiest one: use a family provider. I’m sure each ClientDetailScreen can yield a unique identifier, which can be the family key. Every provider will have its own lifecycle and state.
Slightly harder one, but more "Provider-package"-like: use a child ProviderScope, down just above the ClientDetailScreen and override the provider, much like you probably already had with Provider-package.
The latter is still supported, but discouraged, because families are far more flexible, especially with code-gen.