I'm relatively new to Flutter and Cubit pattern and I'm trying to figure out which are the best ways to work with them. Recently my colleague and I have been struggling to reach an agreement where we should inject the cubit and the repositories.
Reading the bloc/cubit documentation, it is not very clear about where we should do it.
IMO, everything that we need to instantiate, should be injected as high in the tree as possible where two different components that will use this information have in common.
For my colleague, each widget can instantiate one cubit, meaning that each widget will have its own instance of the cubit.
I would like to discuss about what are the community thoughts and best practices regarding the dependency injection and architecture regarding cubit.
There is no single answer to that question. It all depends on your project structure and architecture. In general though:
It's OK to create a few cubits/blocs in one screen/widget. Some widgets or screens contain more than one business logic stuff. Cubits are just classes that help you maintain the state, but it's no different than having many animation controllers or text editing controllers, it's just that it serves a more high-level state management. Let's say that you have a comments section in your app. You may have:
a cubit for the comments themselves, to load them, load more on scroll, report error when loading failed etc.
a cubit for each comment that manages the "Like" button under a comment
It's perfectly valid to have it that way.
It's OK to have global cubits for the whole app. There are some things that you need to have access to from the whole application. It usually is navigation (Navigator), and some theme management (Theme), why not something more business-logic related then, like authentication logic, current user context, user's app preferences, etc.? :)
IMO, everything that we need to instantiate, should be injected as high in the tree as possible where two different components that will use this information have in common.
This is a good approach. Most frequently it will be above your routes, so somewhere above your MaterialApp. If you make use of nested Navigators, then this common place could be above this nested Navigator.
On a more technical side, how will you manage the dependencies used in those cubits/blocs is up to you and your liking. I find some of the options:
Instantiating all repositories and other dependencies in main.dart method and then passing them in constructors to your blocs/cubits in Providers.
To reload those dependencies you will need a Hot Restart though, Hot Reload won't be enough.
Putting your dependencies in the widget's tree with Provider, just like blocs/cubits.
Using riverpod instead of provider.
Using a Service Locator pattern with get_it and injectable combo.
The most correct choice will be something that you (and your colleagues) are most comfortable developing with and that scales well.
Related
I've been coding with dart and flutter for over a month now, one thing that I was recommended to do by my friend is to learn Bloc as it would be helpful for big projects for businesses. From watching Videos on Youtube, reading documentations on it and also looking at Github repo's I've still not understood why we need to use Bloc and its features (FYI: I've only started using Bloc the last 2 days') Would appreciate if someone would explain what's the purpose of using Bloc as opposed to just normally coding.
Benefits of using Bloc pattern
It follows SOLID principle.
Your business logic is decoupled from the UI.
You can use same Bloc object in various screens. For example if you are developing e-commerce app, you might want to show cart icon with items added on many screens and items could be added to cart from any screen, you don't have to pass around Cart object or Function to addToCart.
You instantly get notified when Bloc state is changed and you can build your widget based on new state. This also handles unnecessary builds.
You can change any implementation of business logic (like form validation) in the bloc and your UI will not need any change.
Adding Analytics to the app is also convenient.
Less logic in Widget classes means easier readability.
You can transform events passed to Bloc. (in case if you are showing search bar and on every letter type, you are making a network call to show search suggestion, you can add debounce to certain time).
You don't have to check for Authentication in every screen.
You can respond independently to various state changes of Bloc for showing Snackbar/Toast in any screen.
Less number of items passed while creating widget class (as many properties can be accessed from Bloc object)
Follows MVVM pattern which says model, viewmodel and view should be independent of each other so that future modifications/ scaling/ debugging is easier.
There could be many more benefits which others can tell. You will only realize them once you would have started using the Bloc after working with a project which does not have state management.
Sidenote: flutter_bloc library is an option to implement Bloc pattern. Bloc pattern was showcased by Google and you can write your own implementation as well. But the flutter_bloc library is very robust.
BLoC or any state management protocol will only help you to separate data and presentation layer in your project. It will prove useful when a company has to grow its tech team or if they are planning to separate front-end and back-end development.
There is setState and moreover Provider, using which you can manage your states easily and neatly, then why Riverpod?,
I see different examples in enter link description here where riverpod is being used, I just find each example making simple things more complicated, when you use Riverpod, same things can be done more easily with Provider or just using setState and following some good techniques while managing states in codes.
there is a package named hooks_riverpod, I don't find the justification of this package just to support riverpod you hacked all the statndard widgets although there is another version flutter_riverpod but using hooks is not an intended approach maybe helpful for people coming from reactjs background but flutter engineers have not designed flutter this way, using these non-standard approaches you are just trapping yourself within the mercy of these few packages.
Inherited widgets is only standard approach given by flutter of managing states across the app, Provider and some other Packages like Redux they just follow the same approach.
If you may have used the Riverpod or related packages please share your experience.
It depends on the project and how you like to handle state. If you are ok with setState management/Inherited widgets, you don't need to use others.
I like share some reference here, On riverpod doc second section you can find
Provider, without its limitations
Riverpod is inspired by Provider but solves some of it's key issues such as supporting multiple providers of the same type; awaiting asynchronous providers; adding providers from anywhere, ...
riverpod, Dart only (NO flutter)
flutter_riverpod, A basic way of using Riverpod with flutter. You mostly use it for state management.
If you are using hook widgets(flutter_hooks) that reduce the boilerplate code like dispose, you can use hooks_riverpod
Also, all these packages provided by same author Remi Rousselet
Well, there's a lot of debate on a good state management solution out there.
But in your context, I'd like to mention some points.
Why Riverpod over Provider?
Well, Riverpod was built to fix some issues of Provider which would have been impossible to fix in Provider.
Like:
Majorly, Riverpod is compile safe.
Solves stuff like multiple providers, adding providers from anywhere.
Removes Flutter dependence, there's no need of using contexts anymore like that were used in Provider.
and others... for more on that you can refer to the home page of Riverpod here
Also, Remi, the creator of Riverpod & Provider suggests using Riverpod over Provider.
Secondly, why not setState?
Well, you can't build a featured application just using setState with proper programming standards. You would have to pass up and down data in your application continuously with Prop Drilling. Imagine having 5 widgets under a parent widget and the parent widget needs the data in the 5th sub widget. This is just a normal case, it could go much worse in actual applications.
About hooks?
Well, yes, it's well easy for React devs to quickly jump on to Flutter. But that's just not the case it was developed for. Its main purpose is to use reusable functional widgets. So, a good example of this will always be, when you're using Animation Controller and you've to maintain its lifecycle every time you use it. I can't go in depth here, for that you can refer to the docs.
Currently am working on Sport Event Scoring app.
The structure of the app goes something like this:
Right now I am trying to include state management using provider package.
Scoring occurs at the very bottom of the Widget tree using a counter and each Widget initializes respectively named model based on a user input on the go - creating Event, adding divisions and players each happens in a dialog.
I know I need to create a provider for list of Events, however, I am not sure how to go about structuring rest of the state management. So far I tried turning each model into a Provider by mixin in the ChangeNotifier and moving all the methods which change the data inside where the state would be managed as well with notifyListeners().
However after a lot of struggling and searching I found that it's not the best approach to manage the state inside models.
What approach would be ideal?
Should I manage the state through the Event List provider, or should I go with creating a provider for each model in a separate file? If so, how?
I am not too sure about the full scope of your project nor how far you are into it but I would highly suggest checking out the BLOC library as an alternative state management solution to simply manage the state in your case.
The learning curve is fairly low with many tutorials on how exactly to use the library alongside the fact that BLOC is much less reliant on listeners from an external persons view using the library from my experiences if that is what you are struggling with.
Here is a link to the BLOC Library
If you are still persistent to use Provider, I would suggest you create a provider for each model in a separate file for multiple reasons such as separation of concerns and ease of management of state and clarity of each different model.
I'm working on a prototype for a storefront, and I have a slider where I have the option to see the user's profile. Every user has to register first before they can access the store (is just a prototype).
When a new user registers successfully, they will be immediately sent to the product catalog screen. On the same catalog there will be a slider, and in that slider there will be an option to see the user's profile page.
My question is, how can I send the user's info (name, email and phone number) through the catalog page, then trough the slider, and then to the user's profile page?
I already have a class that holds the user's info that's created on the RegisterPage class/screen.
Unless I'm reading the question wrong, it sounds like some kind of state management solution would be useful here. That is, once you know the user's details, they should be stored in a place that is easily accessible later on from other widgets.
There are a few state management options available:
StatefulWidget - a classic, simplest approach. Have a StatefulWidget which tracks state (user's details), and pass them down the widget tree to a component that can make use of those details. Gets a little unwieldy if you have a deep tree and need to pass the data down through several layers that don't really care about said data.
InheritedWidget. Propagate information down the widget tree and rebuild UI whenever inherited widget itself changes state. Relatively straightforward, simple solution. Unlike StatefulWidget, you can retrieve required data where you need it, and you don't have to pass it down as arguments to yet another widget.
Provider package. A state management solution by Remi Rousselet, endorsed by Google itself. Easy to use and understand, and quite scaleable. Advertised as:
A mixture between dependency injection (DI) and state management, built with widgets for widgets.
Redux. Another state management solution that comes in a form of a package. If you've worked with React/Redux, you will feel right at home. It's not as full-featured as its JavaScript counterpart, but the idea is the same: have a store to which some widgets are listening, and rebuild them whenever data changes.
BLoC / Rx. One of the Google's recommended ways to manage state. Leans heavily on streams. Is somewhat more complex to understand, but rather powerful once you do.
MobX. Another state management solution ported from JavaScript world. I haven't had much experience with it, but I believe it's based on observables, actions and reactions. I expect it should be quite similar to JavaScript's counterpart.
You can read more about each of those by heading over to the dedicated page on the official documentation.
Personally, I'd probably choose something relatively simple first - perhaps InheritedWidget or provider package. However, it heavily depends on your use case and what programming paradigms you're comfortable with / want to experiment with.
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