Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiProvider does not support above 168 providers #703

Open
tuhinansu-gourav opened this issue Dec 3, 2021 · 31 comments
Open

MultiProvider does not support above 168 providers #703

tuhinansu-gourav opened this issue Dec 3, 2021 · 31 comments
Assignees
Labels
enhancement New feature or request

Comments

@tuhinansu-gourav
Copy link

tuhinansu-gourav commented Dec 3, 2021

Describe the bug
If we add more than 168 providers to Multi Provider then on iOS simulator it does not work.(stack overflow error)
On real device and on Android it works fine

To Reproduce

Just add more than 168 providers and try to run on iOS simulator.
Also please use Navigator.

Expected behavior
It should support more than 168 providers.
If don't use multi-provider, and add more than 168 providers then it works

@tuhinansu-gourav tuhinansu-gourav added bug Something isn't working needs triage labels Dec 3, 2021
@rrousselGit rrousselGit added enhancement New feature or request and removed bug Something isn't working needs triage labels Dec 5, 2021
@rrousselGit
Copy link
Owner

It's not really a bug, rather it's a limitation of the widget tree, which depends on your device

Optimisations are possible, but removing the limit is difficult

@tuhinansu-gourav
Copy link
Author

tuhinansu-gourav commented Dec 6, 2021

@rrousselGit
Can you please locate me the place where this limitation code is present ?

I would like to remove this limitation for my project.

@rrousselGit
Copy link
Owner

There's no particular place.

@tuhinansu-gourav
Copy link
Author

But then why is the problem only happening in debug mode in iOS simulator ?
I have tried in iPhone 13 Pro Max, iOS 15

This is not happening in android !!

@tuhinansu-gourav
Copy link
Author

I found similar issues faced by others. It maybe a limitation of flutter
https://github.com/flutter/flutter/issues/85026
https://github.com/flutter/flutter/issues/73734

But I think this should be fixed some where, either here or by flutter.

@rrousselGit
Copy link
Owner

That "limit" is built directly in the device. It's not Flutter or Provider that decides to throw a StackOverflow error, but the device that tells that it reached its stack limit

@tuhinansu-gourav
Copy link
Author

But shouldn't the framework and packages be built depending upon the device configuration rather than the other way around ?

@tuhinansu-gourav
Copy link
Author

tuhinansu-gourav commented Dec 7, 2021

The problem with provider package is that it increases this hierarchy without considering this limitation.

@rrousselGit
Copy link
Owner

What exactly do you expect provider to do here?

@tuhinansu-gourav
Copy link
Author

I expect that if flutter is not able to support deep hierarchy widget tree, then provider shouldn't keep increasing the widget tree.
Maybe find a better way to do what it is doing but not increasing the hierarchy.
e.g. redux in react

@rrousselGit
Copy link
Owner

Proivders are widgets.
They'll always increase the hierarchy

They would in Redux too. It's just that in Redux, you define a single StoreProvider instead of 168 providers.
You could have one large provider if you wanted to

@tuhinansu-gourav
Copy link
Author

https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
Inherited widgets don't increase widget tree.

BTW, how to have one large provider ?
Can you please give me an example ?

@rrousselGit
Copy link
Owner

Inheritedwidget are widgets, therefore increase the size of the widget tree

BTW, how to have one large provider ?
Can you please give me an example ?

Regroup multiple class into a larger one.

Redux is one monolithic object

@nashihu
Copy link

nashihu commented Jun 29, 2022

you can have "unlimited" provider using this approach:

 MultiProvider(
      providers: [
            ChangeNotifierProvider(create: (_) => AppProvider()),
      ],
      child: MaterialApp(...),
 )

then

 class AppProvider extends ChangeNotifier {

      late HomeProvider homeProvider;
      late SecondProvider secondProvider;

      AppProvider() {
           homeProvider = HomeProvider();
           secondProvider = SecondProvider();
      }
 
 }

in home page you can call

HomeProvider get provider => context.read<AppProvider>().homeProvider;

haven't test for hundreds provider yet, but what do you think? @rrousselGit

@nikhith265
Copy link

is this happen for real devices in release mode ?

@brunovsiqueira
Copy link

Getting StackOverflowError after upgrading to Flutter 3.7 only on iOS. We also use MultiProvider with many global providers. @rrousselGit what is your advice to solve it? Scoping providers? Stop using MultiProvider and using lots of individual providers?

@tuhinansu-gourav said that changing to many individual providers worked.

@rrousselGit
Copy link
Owner

You can stop using MultiProvider yes. That should raise the limit quite a bit, at the cost of readability.

@brunovsiqueira
Copy link

@rrousselGit So, since providers nests each other, the only solution that escalates is not to have dozens of providers above the same widget tree. Am I right?

@brunovsiqueira
Copy link

You can stop using MultiProvider yes. That should raise the limit quite a bit, at the cost of readability.

I don't see why it would raise the limit. Wouldn't the providers be nested anyway?

@rrousselGit
Copy link
Owner

MultiProvider works by adding extra widgets under the hood

Not using it means those extra widgets are no-longer here, which gives you a few extra slots for providers

@brunovsiqueira
Copy link

@rrousselGit if I migrate to Riverpod (I haven't read the docs yet), would it solve the problem of the widget tree? Or Riverpod does have the same InheritedWidget problem for multiple providers?

@rrousselGit
Copy link
Owner

It would solve the problem yes.

Riverpod only uses a single widget for storing providers (ProviderScope).
As opposed to with Provider where every provider counts for 2-3 widgets

@brunovsiqueira
Copy link

@rrousselGit (I can contact you in another way if it is not scope of this issue).
We use ChangeNotifier for almost everything regarding state management in our project. I know in RiverPod you wanted to push immutability, and I think that's pretty nice. I was just wondering if sometime in the future you are thinking about removing ChangeNotifier from Provider?

@rrousselGit
Copy link
Owner

Why would I do that?

@IsaAmante
Copy link

IsaAmante commented Mar 14, 2023

@rrousselGit , do you think keeping the MultiProvider but using lazy: true on the ChangeNotifierProviders would help with this case?

@rrousselGit
Copy link
Owner

No because lazy or not has no impact on the depth of the tree

@Thelm76
Copy link

Thelm76 commented Apr 28, 2023

The issue is known by flutter. There is a fix That permits to "reset" the tree depth, avoiding stackoverflow => flutter/flutter#85026 (comment)

This could be a good fix for provider, wrapping the child in MultiProvider in a LayoutBuilder

@rrousselGit
Copy link
Owner

The problem is, that would make MultiProvider insert RenderObjects – which is UI related. Providers are currently UI-independent at the moment (no RenderObject involved).

But you can do that on your own.
MultiProvider supports inserting a Consumer too:

MultiProvider(
  providers: [
    Provider(...),
    Consumer<Null>(builder: (ctx, _, child) {
       return LayoutBuilder(builder: (ctx, _) => child);
     }),
     Provider(...),
  ],
)

@nikhith265
Copy link

The problem is, that would make MultiProvider insert RenderObjects – which is UI related. Providers are currently UI-independent at the moment (no RenderObject involved).

But you can do that on your own. MultiProvider supports inserting a Consumer too:

MultiProvider(
  providers: [
    Provider(...),
    Consumer<Null>(builder: (ctx, _, child) {
       return LayoutBuilder(builder: (ctx, _) => child);
     }),
     Provider(...),
  ],
)

The following error appears while doing this.

The following ProviderNotFoundException was thrown building Consumer(dirty):
Error: Could not find the correct Provider above this Consumer Widget

@tuhinansu-gourav
Copy link
Author

I think earlier @rrousselGit was just giving an example.
The new error that you are getting is that, a consumer can only access providers that are above it in widget tree.
You can check it in widget inspector.

@rrousselGit rrousselGit self-assigned this May 10, 2023
@nekomaruh
Copy link

If you have too many providers running at the same time, it might be a sign of an issue with your app's architecture. This usually happens when you’re wrapping too many widgets under a single provider or placing them too high in the widget tree.

A good practice is to keep providers as close as possible to the widgets that actually need them. This keeps things more efficient, avoids excessive memory usage, and ensures that only the necessary parts of the UI are rebuilt when something changes.

Also, it’s a good idea to scope providers to the screen level whenever possible. That way, when you navigate away from the screen, the provider gets disposed of automatically, which helps free up resources.

If you’re running into a stack overflow error when adding +168 providers in a MultiProvider on an iOS simulator, it’s likely due to the way the Flutter framework handles widget builds and the platform’s stack size limitations, so having too many providers can lead to a deeply nested tree, which exceeds the stack size on iOS simulators. This is less of an issue on real devices or Android, as they tend to have larger stack size allowances.

There are some tips that could help you:

  1. Refactor Your Architecture: Group related providers under smaller MultiProvider instances scoped to specific screens or features.
  2. Split Providers: Use multiple MultiProvider layers to avoid a single, massive tree.
  3. Use Providers Directly: Add Provider widgets manually to flatten the tree.
  4. Code Splitting: Dynamically inject providers for specific features instead of loading all globally (It's not recommended to have all the providers in the main function).

Another way that I discovered lately, its to create a UiModel, and then if you are updating a single attribute, you can assign a valueNotifier without creating a provider class, for example:

class InputItemUI {
  final String id;
  final ValueNotifier<String> name;

  InputItemUI({required this.id, required String name})
      : name = ValueNotifier(name);

  static InputItemUI fromEntity(InputItem entity) {
    return InputItemUI(id: entity.id, name: entity.name);
  }

  InputItem toEntity() {
    return InputItem(id: id, name: name.value);
  }
}

This is even a good approach when working with clean architecture mapping 3 types of models, one in data, another in domain, and this one in presentation layer.

Hope it helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants