River_pod: Big app architecture doubts

Created on 9 Sep 2020  路  14Comments  路  Source: rrousselGit/river_pod

First at all, thanks a lot Remi for your great contribution to the community!

Describe what scenario you think is uncovered by the existing examples/articles
I have doubts about how use Riverpod on a big app architecture. Specially on how avoid have access to multiple providers for different app features/flows.

Describe why existing examples/articles do not cover this case
For example, if I have the following scenario:

  • Home

    • Flow A

    • Flow D

    • Flow E

    • Flow B

    • Flow C

    • Flow F

When:

  • All flows require access to Home Providers.
  • Flow D and E require access to Flow A providers and create they own.
  • Flow F require access to Flow C providers and create they own.
  • The providers created on the different Flows should not be accessed for his parents or for same level Flows.

Doubts:

  1. How can I encapsulate Flow providers? It's a correct approach create an ProviderScope Widget as parent for every Flow to achieve this? I understand if I use Provider .autoDispose the state of the Provider is disposed, but isn't clear to me "have access" to a Provider functionality if the Flows are completly different among themselves.
  2. If in Flow E I have a .autoDispose Provider with ref.maintainState = true because I really like avoid reload some information and is good maintain the state when I navigate between Pages of the same Flow. How can I clean the state when I leave the Flow? The autodispose I understand not work anymore. I need to clear it manually?

Thanks!

documentation

Most helpful comment

You're probably over worrying.

There is a full blown devtool for Riverpod in progress, so the readability concern of the widget inspector is only temporary

As for preventing access to the state, you could play around making the provider private, and having all the related class/functions in the same file/part
Alternatively you can split your app in multiple packages, with one package per flow.
But that's probably over-engineering

All 14 comments

If you want fine-grained control on when a provider is disposed, do not use maintainState= true.

Instead, watch the provider in a widget that is removed from the tree when the state should be disposed. It could be the nearest common ancestor between all of your wigdets that uses the said provider for example.

Thanks @rrousselGit .
And what you think about this?:

How can I encapsulate Flow providers? It's a correct approach create an ProviderScope Widget as parent for every Flow to achieve this? I understand if I use Provider .autoDispose the state of the Provider is disposed, but isn't clear to me "have access" to a Provider functionality if the Flows are completly different among themselves.

I don't really understand the question to be honest.

Ja! I will try to explain myself better.
Suppose the following scenario:

  • Home (home.dart - final homeProvider = Provider((ref) => 'Home');)

    • Flow A (flow-a.dart - final flowAProvider = Provider.autoDispose((ref) => 'Flow A');)



      • Flow D (flow-d.dart - final flowDProvider = Provider.autoDispose((ref) => 'Flow D');)



Ok, with this, Flow D can access to homeProvider, flowAProvider and flowDProvider. Then, when I leave the Flow D, the state of flowDProvider will dispose because .autoDispose, right?
But, because I declare global flowDProvider on flow-d.dart, the reference of flowDProvider is alive on the ProviderContainer from main ProviderScope? Can I access to them on home.dart and use "his functionality"?
I understand if I use Provider .autoDispose the state of the Provider is disposed, but isn't clear to me if I "have access" to a Provider functionality on other parts of the app once the Provider has already been loaded at another time.
This is the approach I don't understand. My idea is to be able to encapsulate the Providers so that a parent or same hierarchy files cannot reference it.
If I have many flows, I would not like that by browsing all of them there are provider references that I will no longer use.
I don't know if I make myself understood, I probably have some concepts wrong, that's why my question.

Exemplifying the question I realized that if I want to use a child provider from the parent, I have to import said file which would be very bad, I know. My question is more conceptual from the sense of whether there are references from all the providers that I was using, that is what does not convince me.

To encapsulate and make sure you don't access flowDProvider outside of flow-d.dart you can just change the variable name to _flowDProvider to make the definition private to the file. That is if I'm understanding the question properly. Otherwise any file that imports flow-d.dart might access the provider. This is the only way to enforce limited access to a provider as far as I know.

To encapsulate and make sure you don't access flowDProvider outside of flow-d.dart you can just change the variable name to _flowDProvider to make the definition private to the file. That is if I'm understanding the question properly. Otherwise any file that imports flow-d.dart might access the provider. This is the only way to enforce limited access to a provider as far as I know.

It doesn't work in this case
A

B (_bProvider)

C (want to access _bProvider)

Put it in a file called bProvider.dart and just be careful about imports. That's the only solution I can think of. There is no other way of restricting access to a provider that I know of.

What are you trying to achieve by making a provider unreadable for a route?

I have two fears:

1) Readability and tracing providers states: If I saw Providers not used anymore in the ProviderScope, when user navigate for different Pages from different Flows, will be easy identify the states of the important Providers in use?
This is the ProviderScope providers for Marvel example:
Screen Shot 2020-09-09 at 18 05 09

I'm afraid how will it be in a large application. That is why I was thinking of containers for isolate flows by hierarchy.

2) "Security". It's not something concrete but I don't really like allowing, for example, be able to access functionality that implements on some specific Provider of another flow.

Essentially are fears and doubts presented when think a clean architecture for a large applications using Riverpod.

You're probably over worrying.

There is a full blown devtool for Riverpod in progress, so the readability concern of the widget inspector is only temporary

As for preventing access to the state, you could play around making the provider private, and having all the related class/functions in the same file/part
Alternatively you can split your app in multiple packages, with one package per flow.
But that's probably over-engineering

You're probably over worrying.

There is a full blown devtool for Riverpod in progress, so the readability concern of the widget inspector is only temporary

Great! 馃槃

As for preventing access to the state, you could play around making the provider private, and having all the related class/functions in the same file/part
Alternatively you can split your app in multiple packages, with one package per flow.
But that's probably over-engineering

Mmm... This seems kind of ugly to me, but probably as you say is to over-worry.

Allow me two more questions:

  • Do you know how much does it cost (I supose is only in memory) each global Provider variable without State? ("cleaned" after .autoDispose).
  • If I have PageA Widget with a .autoDispose Provider, when I push a new Page the Provider lost his state? Or while PageA is in Navigator stack all Providers whatched on others Pages before are alive?

Thanks a lot!

Do you know how much does it cost (I supose is only in memory) each global Provider variable without State? ("cleaned" after .autoDispose).

Basically nothing.
After a dispose, only the Provider instance is remaining, which is stateless and is nothing but an object that holds a function.

If I have PageA Widget with a .autoDispose Provider, when I push a new Page the Provider lost his state? Or while PageA is in Navigator stack all Providers whatched on others Pages before are alive?

Providers are destroyed only if you "pop" the route that listened to the provider.
So unless you used "pushReplacementNamed", the provider would be kept alive.

Is that undesired?

Do you know how much does it cost (I supose is only in memory) each global Provider variable without State? ("cleaned" after .autoDispose).

Basically nothing.
After a dispose, only the Provider instance is remaining, which is stateless and is nothing but an object that holds a function.

If I have PageA Widget with a .autoDispose Provider, when I push a new Page the Provider lost his state? Or while PageA is in Navigator stack all Providers whatched on others Pages before are alive?

Providers are destroyed only if you "pop" the route that listened to the provider.
So unless you used "pushReplacementNamed", the provider would be kept alive.

Is that undesired?

No, it is exactly what I expected :D . It may be a good idea to put this clarification in the .autoDispose documentation. Thank you very much Remi, I close the issue!

Was this page helpful?
0 / 5 - 0 ratings