Example of why state management approach that doesn’t need a BuildContext is bad - flutter

There are so many Flutter state management approaches https://flutter.dev/docs/development/data-and-backend/state-mgmt/options
To avoid opinionated response, can someone provide a concrete example that supports why state management approach that doesn’t need a BuildContext is "bad" or "red flags"
One argument to use a state management package without BuildContext is this:
The fact that you need a BuildContext to access your objects made it
inaccessible from the Business layer.

Technically, there is no state management that does not need a BuildContext.
A widget does not exist by itself in Flutter. A Widget is accompanied by an Element. The Element maintains the position of the widget in the widget tree. If the element is mounted, then the widget is present in the widget tree. When the element is unmounted, it gets removed from the widget tree permanently. The Element contains functions like update, rebuild, performRebuild, updateChildren and many other functions that are used by the framework to manage the lifetime and under-the-hood behaviour of widgets, for updating state and for many other functionalities. You always need this Element to update the state of the corresponding Widget. Both StatelessWidget and StatefulWidget uses this element to re-render.
Surprise, surprise, the Element is actually an implementation of BuildContext as seen in framework.dart
I know that you meant using BuildContext to retrieve the object representing the state. This is more of a design choice than a technical requirement that package authors have to make. Essentially, you are using context to get the state object instance that were associated to the corresponding widget tree branch, by some parent widget. That's it. And you are doing that instead of the otherwise approach of passing around the class instance that represents state to children widgets. Both are "ok" approaches to state management.
Limiting to using the context alone is honestly in my opinion, a poor design decision. I personally believe that using context everytime to be able to incorporate state management, is just tideous. In many cases I have seen countless of apps that instantiate the Controller or ChangeNotifier only once and inject it to the root-level widget. Essentially using only one instance. One major purpose of using BuildContext is that you can provide multiple instances to multiple array of children and thereby differentiate their behaviour. But the reality is that a vast majority of apps DO NOT NEED this functionality. Another bonus of using Context is that you can retrieve object instances (of Controllers, ChangeNotifiers...) dynamically like Provider.of<MyClass>(context), without passing them around all the time. Essentially this is an example of dependency injection. But this is more of a poor design decision than great technical feat, as the source of your class instance is ambiguous. You only know the type of the instance. But ultimately, everything is personal preference, and if you are working, is often up to people who manage you to decide what architecture and state management solution to use. There are apps written using all of these "correct" approaches to state management.
You can checkout Turbo by the way. It is a very simple & efficient approach to state management I created, that does not use contexts to maintain and update state, at least not yet. It also has one of the easiest event systems so as to subscribe your widgets to only specific events.

Related

Providing BLoCs - Global vs Specific

I am currently learning to use the BLoC pattern for state management and architecture for Flutter and Dart.
I have come across 2 ways of providing access to a BLoC in a widget.
1 - Use BLoCProvider or BLoCProvider.value when accessing a widget and passing it to another screen.
2 - Wrap your MaterialApp widget in a BLoCProvider to provide access to a bloc globally.
It seems that using the 2nd option would always be easiest - a single place to manage your BLoCs, no issues with build context referencing, ensuring a single BLoC instance, and allowing global access!
Are there any drawbacks to creating and providing all an applications BLoCs in this way? Are there any performance issues, etc?
I answered this briefly in another question yesterday.
In short:
blocs that need to be globally accessible should of course be. This could e.g. be a bloc that is handling authentication or notifications...
other blocs should not be globally accessible, this could e.g. be a bloc that handles fetching information from a backend service etc. I'd love to hear any good reasoning why a screen far down a widget tree that handles something special should have it's bloc globally accessible that any other widget could access...
Having all blocs accessible globally can have a negative effect on performance and it would break common good programming practices.
Edit:
This is from the creator (Felix Angelov) of flutter bloc:
The main disadvantages of providing all blocs globally are:
The blocs are never closed so they are consuming resources even if they aren't being used by the current widget tree
The blocs can be accessed from anywhere even if the state of the bloc is scoped to just a particular feature
The blocs typically end up needing some sort of "reset" event to revert back to the initial state which is not necessary if they are
properly scoped and automatically disposed by BlocProvider
My recommendation is to create a bloc per feature and provide that
bloc only to the specific subtree that needs it. Hope that helps

Is it a best practise to pass provider as an argument to widgets?

Is it a best practise to pass provider as an argument to widgets?
I am aware that we can directly access provider from within the widget.
But saw a couple of code snippets passing provider as an argument to child widget.
Does it optimize the code? Or any possible leaks?
It is better to access the provider from within the widget that requires it, since the Provider uses the BuildContext (as in Provider.of(context)) to fetch an instance of the requested provided service, based on the location of the widget in the widget tree as it traverses the hierarchy above to find it. You can do it for convenience, but widgets should be encapsulated enough and decoupled enough so as not to depend on higher above widgets to get data fed to them, making them self-sufficient and independent. My two cents.

How should I manage the state of a large model in Flutter?

I'm writing my first Flutter app and struggling with the variety of state management solutions. I decided to start with Provider, but I'm thinking about switching over to BLoC. So far most of the examples I've found are limited to relatively simple things, like showing a list of items or responding to some button presses. In my case, almost all of the app is focused on setting up a rather large hunk of data. (It's basically a whole bunch of forms all working on different bits of a large data structure.)
At the moment, all of the state management is put together into one provider class because most of it is very closely related. For instance, the biggest part of it is a list of items, and then a bunch of subsets of that list. The majority of the data manipulation in the app is on those subsets.
I wasn't really intending to do this in the beginning, but I've found myself putting the code that actually uses the provider pretty close to the top level, and then passing data down the tree. It goes against the whole point of Provider, but it's resulted in less duplicated code. For example, one of the screens in my app has a bunch of cards that all contain very similar lists. The main difference between them is that their contents comes from different lists. So in order to reduce duplicated code, I generalized the cards and lists as much as possible, and passed the necessary data down the tree. I'm also passing down callback functions to handle things like editing an item or removing it from the list.
Does this sound like an okay way to go about it? The fact that I'm passing state and callbacks down the tree feels like a code smell to me, but as I mentioned, it's resulted in less duplicated code. What about the fact that I have a single provider that exposes a lot of stuff? Part of me feels like I should have the data model separated from the provider, and then have multiple smaller providers. If I were to go that route, would BLoC be a better fit than Provider? Would there potentially be a performance boost if I'm able to break up the single provider? Having multiple providers or blocs seems like it could complicate saving changes, but maybe not. Have you ever found that to be an issue?
EDIT: I just found this question, which talks about reusing providers for different data: Flutter Provider. How to have multiple instances of the same provider type?
I hadn't really thought of it like that before, but that's a big part of what I'm struggling with. I have different lists that are modified independently. It's the same widgets that are used in multiple places (sometimes on the same screen), and the functionality in the provider would also be the same if I broke what I have up into smaller pieces. Since Provider is based on type rather than instance, maybe it just isn't going to work in this case.
My first Flutter app used a single Provider class and it worked but was a giant messy class and triggered tons of UI updates.
Currently I use a "models" folder for data models and "services" folder for Provider singletons.
Models has all my data classes with utility and mutator methods. For example a MyCard class to hold values plus MyCard.fromJson() and .toJson() for serialization, and maybe some routines like clear() and validate().
Services is where I create Providers as singletons. For example, a class MyCardService with ChangeNotifier singleton that has a List<MyCard> to hold all the MyCards. If I want a UI widget to update based on changes, like when MyCardService().loadCardsFromNetowrk() completes, I access the service in a build() method using Provider watch or select. If I want to access data outside the build() chain, I access it with the singleton pattern: MyCardService().myVariable. This removes the need for context lookups outside the build() chain.
This provides a lot of control for async access. For example you can have a mutex on get and set methods, a Future<bool> isInitialized that resolves as true or false after your service tries to initialize. You can await this future in any async call that needs to access the service or a FutureBuilder()
I also have some non-Provider service singletons that other services use for stuff like network API access and filesystem caching.
The provider services are mostly organized by UI data consumption. This way a notifyListeners() doesn't trigger UI rebuilds for unmodified widgets (a major problem with my first single-file provider project that resulted is a bunch of individual variables using select instead of one watch).
In the above example, the Provider singleton holds a List and all the methods for working on the List<>. The MyCard data model holds all the methods for working on a MyCard. When I have a widget that edits a single MyCard, I use a stateful widget that does a MyCardService().save(myCard) when editing is finished. This also helps isolate UI tree rebuilds.

Using Flutter Provider means no StatefulWidgets?

I'm getting ready to write my first nontrivial app with Flutter and Provider. I've read up on how Provider facilitates immutable widgets (StatelessWidgets). My question is, is it always an antipattern to use StatefulWidgets when using Provider? If not, what are examples of when it better to use StatefulWidgets in a Provider app?
EDIT
It's been a couple months using Provider and I'm still favoring it over StatefulWidgets in every case. Every now and again I introduce a StatefulWidget, mostly to try to gain familiarity with them, and almost immediately regret it and refactor to Provider. The other day I ran into widgets not refreshing because they were identical types, so was looking at introducing keys so they would refresh. First couple attempts failed, so I refactored into Provider and everything just worked (without the need for keys).
Antipattern was not the proper term in my OP. I guess my question is, are there examples where StatefulWidgets are cleaner or otherwise easier/better to use?
provider doesn't care whether you write stateless/stateful or anything else (hooks?).
It removes the need to write a StatefulWidget in many situations, but it doesn't claim that you should use StatelessWidget only.
In the end, it's your job to decide if you need a StatefulWidget or not. You may need it when writing animations for example.
Adding to Rémi's answer and new to this implementation:
use Provider for shared models
use widget state to manage the model that is specific to that concern when it's needed
imagine a user object after auth, null before, shared through an app, with a form with state specific to editing fields, like a nickname, or whatever, updating local state and possibly propagating out to the rest of the product (when finished updating on the backend?...who knows) and that state is disposed of when that view isn't needed anymore, but the user reference remains via the provider reference. It doesn't make sense to manage all that state change in the provider model--that's where the result goes.
Making a few assumptions here based on my experience with React+Redux as well as passing objects around a native Web Components (similar lifecycle to both of these) as well as LitElement. The patterns seem similar if not the same so-far.
Provider.of<ProviderClass>(context, listen: false);
The Provider is one of state management mechanism which Flutter has, under the hood, Provider keeps track of the changes which are done inside the widget. It doesn't matter to Provider whether it's a Stateless widget or Stateful widget, It's gonna rebuild widget if anything gets change.
listen: false tells the Provider not to rebuild widget even if data gets modified. That means it can only re-build if the parent widget gets modified by setState() or by the ProviderClass class value gets modified.

Provider vs. Get_it

Searching for Dependency Injection solutions for Flutter, I found two awesome libraries: provider and get_it.
As far as I can see, provider has more boilerplate, but it fits really nicely with Flutter, allowing Consumer to rebuild parts of the Widget tree, once an injected value change.
get_it on the other hand is more straightforward, easier to use, and not dependant on Flutter, so can be used with any Dart code.
Are there any more differences and limitations between them? I know this is kinda opinionated, but Flutter is so new that it's good to register publicly benefits, side-effects and gotchas.
The main difference between both is that provider is not strictly dependency injection.
By using widgets, provider is also able to:
providers are compatible with the Flutter devtool
know when a variable cannot be accessed (scoped to a tree)
know when to create and dispose of an object
synchronize model -> model and model -> UI
override some values for only a specific widget tree
voluntarily prevent circular dependency
All of these are, while optional, good for the health of your app in the long run.
It ensures that you're always up to date, makes it harder to have "spaghetti code", and makes your different elements more composable.
I am explaining just one limitation which I practically found, there may be others too.
After searching many tutorials and topics on Get_it that why people use Get_it() even we have dependency injection in the provider, I was unable to understand the difference in terms of DI. Then I stuck in a Scenario and find the answer to your question that "what are the limitations".
Are there any more differences and limitations between them?.
Scenario:
I had Nested widgets, Widget A has Widget B and Widget B has Widget C, I was using the provider and was accessing values in each widget whenever value changed. It was great, Then I make a new widget D which was a separate widget, it was not inside the widget A hierarchy. But when I try to access the same value in Widget D it was not changing. Because widget D is not in the tree of Widget A. Now here comes the limitation of dependency injection of the provider.
Conclusion
You will use Get_it to access values out of the tree widget. But you can't access the
updated value using provider
Updated Answer
In the above scenario, you need to wrap the app with Provider to access all dependencies.
Get It is not a dependency injection solution but a service locator.
It's useful if you want to rapidly switch between two or more implementations of a class. For example to mockup a service, and change between the "real" service or the fake one (for debugging purpose).
Indeed it can't retrieve/supply reference to an existing object (exception is for singleton, but you can do the same by yourself without much more effort) and can supply only new objects.
from streaming through various tutorials what I learned is that the get it package can be called a global variable that can be accessed from any widget to any widget whether nested or not nested VS provider which can only be accessed between the nested widget.
The example is better explained by
M.ArslanKhan