Is it bad practice to create multiple instances of the same bloc/cubits in the tree? I'm creating an instance of the cubit and using it in a few locations in the tree-like so.
BlocBuilder<BlocA, BlocAState>(
cubit: blocA, // provide the local cubit instance
builder: (context, state) {
// return widget here based on BlocA's state
}
)
... but the blocs are scoped to a single widget with this method. I'm using this technique 3 times in the widget tree (splitting up my widgets into different classes). My console is spitting out several instances of the bloc in question from the BlocObserver. The blocs "work" but I'm suspicious I'm going about things the incorrect way...
Seems like you misunderstood the core concept of providing a bloc instance to your widget tree. You need BlocBuilder to set up the BuildContext for your widgets, that depend on bloc, and to minimize the scope of widgets, that will be marked for rebuild on bloc changes.
Yes this is very bad practice to have multiple bloc instances as it will almost nullify its usefulness to your app. To provide a single instance to your widgets use BlocProvider or BlocProvider.value, more about it you can check here or here
Related
Assume that class A is arbitrarily rooted, and its children are classes B, C, and D.
(Not all classes are in the same hierarchy)
When I use BlocConsumer or BlocBuilder in class D, I get an error.
I just want to reuse the Bloc state used in class A and class B.
is there any solutions? thanks
--UPDATE
I am talking about this https://github.com/felangel/bloc/issues/74
Is there any way other than passing it as navigator argument to class one by one?
Note: I assume you are using the flutter_bloc library, but the concept is the same even though you are not.
The BLoC you want to access must be provided ABOVE all the screens/pages you want to access this BLoC in. The simplest solution to your problem would be wrapping your root Widget (probably MaterialApp) with BlocProvider:
runApp(
BlocProvider<YourBloc>(
create: (_) => YourBloc(), // create your BLoC here
child: MaterialApp(...),
),
);
For more info, I would recommend watching this video: https://www.youtube.com/watch?v=laqnY0NjU3M
introduce blocprovider on main myapp page and use it on other pages
So i am writing a Flutter application that utilize the MVVM architecture. I have a viewModel for every screen(widget) with ValueNotifiers and i want to initiate the viewModel for that view.
Now most guides suggest a Provider approach, but why provide it when i can just normally initiate it.
Code:
class FooModel{
final ValueNotifier<bool> _active = ValueNotifier<bool>(false);
ValueNotifier<bool> get active => _active;
FooModel(){_active = false;}
doSomething(){_active=!_active}
}
What i want to do:
#override
Widget build(BuildContext context) {
_viewModel = FooModel();
return Scaffold(
body:ValueListenableBuilder<bool>(
valueListenable: _viewModel.active,
builder : (context,value,_){
if(value)return(Text("active");
return Text("unactive");
}
)
}
what is suggested:
Widget build(BuildContext context) {
return Provider<FooModel>(
create: (_) => FooModel(),
builder: (context, child) {
final vm = Provider.of<FooModel>(context);
return ValueListenableBuilder<bool>(
valueListenable: vm.active,
builder: (context, value) {
if (value) return Text("active");
return Text("unactive");
});
},
);
}
Now i understand that what i suggested creates the viewModel with every build, but that should only happen when screen is loaded thanks to ValueNotifier so its fine.
I guess i just don't understand the value of providing the viewModel.
Flutter has a different ideology.
yes, you can create Value Notifier and it's fine to do that but just thinking of the bigger picture.
Check this flow you want to call an API then perform parsing and filtering on that and you have 2 views on the screen to show the same data one is to showcase the data and the other one is to interact with data and this update needs to be reflected on showcased data.
to do this what we need to do?
create valuenotifier at class level that encloses both screen widgets.
Call API and filter code at the class level.
pass this valuenotifier to both screen widgets you may ask why right? well because one class need to update other class widgets. and that's only one way to push updates to the valuenotifier is the object itself. so you will need to pass this valuenotifier in both classes.
once you do that and update has been synchronized if any setState has been called to the main widget that encloses both of this widgets then you need to do all this again.
also there will be multiple instances of valuenotifier which is bad as valuenotifier is a stream and you need to close your streams once you're done with the stream so you will be needing logic to close your streams at any setState event on main widget.
What is provider exactly? and how it works? well, the provider is a change notifier class which calls setState when you call notifyDataChanged or notify method. this triggers the widget state change which is listening to that data changes.
and that widget gets rebuild. This is the same logic for each and every state management library out there Bloc, ScopedBloc, or any widget like streamBuilder or ValueListenableBuilder.
In Flutter if you want to change data you just need to call setState. Just to be testable, more readable and maintainable what we will be doing is to separate logic into different files just like we do in Android or iOS and that's where this type of libraries comes into the picture to reduce our headache of typing code all over again and focusing on the main task which is the functionality of the app.
See we can create loops in different formats
for(int i=0;i<length;i++)
while(i++<length)
for(i in 0...length)
It's up to us to provide clean and understandable code so that any other developer does not need to delete all code just because he isn't able to understand our code.
There's nothing right and wrong here in development. It's matter of what is more convenient or what makes more sense. like int i does not make sense but if we replace it with index or listIndex it will.
Also, one thing to mention is what you're doing is creating a model that is kind of the same as bloc pattern. so you're already halfway through. you just need to call state change from model check bloc and other patterns you will understand.
I'm still wrapping my head around state-management techniques in flutter and am a bit confused about when and why to use Provider.of<X> vs. Consumer<X>. I understand (I think) from the documentation that when choosing between these two you would use Provider.of when we want access to the data, but you don't need the UI to change. So the following (taken from the docs) gets access to the data and updates the UI on new events:
return HumongousWidget(
// ...
child: AnotherMonstrousWidget(// <- This widget will rebuild on new data events
// ...
child: Consumer<CartModel>(
builder: (context, cart, child) {
return Text('Total price: ${cart.totalPrice}');
},
),
),
);
Whereas, where we only need the data on don't want to rebuild with UI, we'd use Provider.of<X> with the listen parameter set to false, as below:
Provider.of<CartModel>(context, listen: false).add(item); \\Widget won't rebuild
However, listen isn't required and so the following will run too:
Provider.of<CartModel>(context).add(item); \\listener optional
So this brings me to a few questions:
Is this the correct way to distinguish Provider.of<X> and Consumer<X>. Former doesn't update UI, latter does?
If listen isn't set to false will the widget be rebuilt by default or not rebuilt? What if listen is set to true?
Why have Provider.of with the option to rebuild the UI at all when we have Consumer?
It doesn't matter. But to explain things rapidly:
Provider.of is the only way to obtain and listen to an object.
Consumer, Selector, and all the *ProxyProvider calls Provider.of to work.
Provider.of vs Consumer is a matter of personal preference. But there's a few arguments for both
Provider.of
can be called in all the widgets lifecycle, including click handlers and didChangeDependencies
doesn't increase the indentation
Consumer
allows more granular widgets rebuilds
solves most BuildContext misuse
Provider.of<>
applying provider, whole widget will rebuild if listen true.
Consumer<>
using consumer only specifically allowed widget will rebuild.
For your questions:
Is this the correct way to distinguish Provider.of<X> and Consumer<X>. Former doesn't update UI, latter does?
Provider.of<X> depends on value of listen to trigger a new State.build to widgets and State.didChangeDependencies for StatefulWidget.
Consumer<X> always update UI, as it uses Provider.of<T>(context), where listen is true. See full source here.
If listen isn't set to false will the widget be rebuilt by default or not rebuilt? What if listen is set to true?
Default value is true, means will trigger a new State.build to widgets and State.didChangeDependencies for StatefulWidget. See full source here.
static T of<T>(BuildContext context, {bool listen = true}).
Why have Provider.of with the option to rebuild the UI at all when we have Consumer?
Pretty much covered by Rémi Rousselet's answer.
There should not be any performance concern by using it, moreover, we should use consumers if we want to change some specific widget only on screen. This is the best approach I can say in terms of coding practice.
return Container(
// ...
child: Consumer<PersonModel>(
builder: (context, person, child) {
return Text('Name: ${person.name}');
},
),
);
Like in the above example, we are only required to update the value of the Single Text Widget so add consumers there instead of Provider which is accessible to other widgets as well.
Note: Consumer or Provider update the only reference of your instance which widgets are using, if some widgets are not using then it will not re-drawn.
The widget Consumer doesn't do any fancy work. It just calls Provider.of in a new widget, and delegate its build implementation to [builder].
It's just syntactic sugar for Provider.of but the funny thing is I think Provider.of is simpler to use.
Look at this article for more clearance
https://blog.codemagic.io/flutter-tutorial-provider/
We have 3 things to understand here.
When you wrap Provider around a widget it sets up a reference to a widget tree and a variable whose changes you want to refer to.
using Provider.of(context) you can get access to the variable you want to monitor and make changes in it.
Provider.of(context) with and without listen gives you a reference to the above-declared Provider object and a widget tree where it can be accessed from. But as said by others, it rebuild the whole widget tree it sits on top of when listen is not false.
In the end, you can use consumer to monitor any changes that happened using the above step
The consumer acts like a more granular listener and be applied to a fixed widget to help avoid unnecessary rebuilds.
it's just a personal preference and depends on how you understand and use provider.
so the way i think of provider is it's just an object that is providing a ChangeNotifier Object that has Code and Data down the widget Tree.
So,I use :
Consumer<T>
When i want to listen to changes in Data and update/rebuild my UI according to those changes.
Provider.of<T>
When i just want to call the Code. with the listen parameter set to false.
if your problem is to know what the difference is, here is a track
Consumer
reload consumer-only child widgets
Provider.of (with listen true)
reload from the last buildcontext found in the tree
actually in a simple example
exemple1 provider.of
in exemple1 when I click on my container, his increase his size ( gesturedetector send a newvalue at h variable , h variable is in function named method in provider)
with 'provider.of' flutter rebuild at the first Buildcontext below #override
that mean the totality of tree are rebuild
exemple2 consumer
in exemple2 when I click on my container, his increase his size ( gesturedetector send a newvalue at h variable , h variable is in function named method in provider)but with consumer only sélectionned widget are rebuild
that mean one widget are rebuild the other widget doesn't "move"
I hope I could help you
I am using Provider. I have got two classes: class TenderApiData {} it's stand alone class (not widget). How I can write accesstoken to AppState?
class AppState extends ChangeNotifier // putted to ChangeNotifierProvider
{
String _accesstoken; // need to fill not from widget but from stand alone class
String _customer; // Fill from widget
List<String> _regions; // Fill from widget
List<String> _industry; // Fill from widget
...
}
I need way to read\write accesstoken from stand alone classes.
Or I have issue with architecture of my app?
Here is full source code.
You cannot and should not access providers outside of the widget tree.
Even if you could theoretically use globals/singletons or an alternative like get_it, don't do that.
You will instead want to use a widget to do the bridge between your provider, and your model.
This is usually achieved through the didChangeDependencies life-cycle, like so:
class MyState extends State<T> {
MyModel model = MyModel();
#override
void didChangeDependencies() {
super.didChangeDependencies();
model.valueThatComesFromAProvider = Provider.of<MyDependency>(context);
}
}
provider comes with a widget built-in widgets that help with common scenarios, that are:
ProxyProvider
ChangeNotifierProxyProvider
A typical example would be:
ChangeNotifierProxyProvider<TenderApiData, AppState>(
initialBuilder: () => AppState(),
builder: (_, tender, model) => model
..accessToken = tender.accessToken,
child: ...,
);
TL;DR
Swap provider for get_it. The later does DI globally without scoping it to a BuildContext. (It actually has its own optional scoping mechanism using string namedInstance's.)
The rest...
I ran into a similar problem and I believe it comes down to the fact that Provider enforces a certain type of (meta?) architecture, namely one where Widgets are at the top of what you might call the "agency pyramid".
In other words, in this style, widgets are knowledgable about Business Logic (hence the name BLoC architecture), they run the show, not unlike the ViewController paradigm popularised by iOS and also maybe MVVM setups.
In this architectural style, when a widget creates a child widget, it also creates the model for the widget. Here context could be important, for example, if you had multiple instances of the same child widget being displayed simultaneously, each would need its own instance of the underlying model. Within the widget or its descendents, your DI system would need the Context to select the proper one. See BuildContext::findAncestorWidgetOfExactType to get an idea why/how.
This architectural style is the one seemingly encouraged by plain vanilla Flutter, with its paradigms of app-as-a-widget ("turtles all the way down"), non-visual widgets, layout-as-widgets and InheritedWidget for DI (which provider uses I believe)
BUT
Modern app frameworks libs (e.g. redux, mobx) encourage the opposite kind of meta-architecture: widgets at the bottom of the pyramid.
Here widgets are "dumb", just UI signal generators and receivers. The business logic is encapsulated in a "Store" or via "Actions" which interact with a store. The widgets just react to the relevant fields on the store being updated and send Action signals when the user interacts with them.
Which should you use?
In my experience, at least on mobile where the screen realestate is less, scoping a model to a branch in the render tree is seldom required. If it suddenly becomes important then there are plenty of other ways to handle it (indexed array, id lookup map, namedInstances in get_it) than to require linking it to the semantics of UI rendering.
Currently, having spent too much time in iOS ViewControllers, I'm a fan of new systems which enforce better SoC. And personally find Flutter's everything-is-a-widget pardigm to appear a bit messy at times if left untended. But ultimately it's a personal preference.
you can use navigator key
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
and put this key in MaterialApp and wrap it with your provider (TenderApiData)
ChangeNotifierProvider<TenderApiData>(
create: (_) => TenderApiData(),
child: Consumer<TenderApiData>(builder: (context, tenderApiData , child) {
return MaterialApp(
navigatorKey: navigatorKey,
title: 'title',
home: SplashScreen());
}),
);
and listen to this provider from anywhere with this navigator key
navigatorKey.currentContext?.read<TenderApiData>();
I would like to know the best way to pass the bloc. I read about the bloc providers, but what`s the difference between using them and just passing the bloc in the constructor like:
ExampleView X = ExampleView(bloc,...)
Actually I find this way easier to test and also a better way to keep the code cleaner. For example, if I have more blocs, something like this can happen:
XBlocProvider(
bloc: XBloc,
child: YBlocProvider(
bloc: Y,
child: ZBlocProvider...
)
or maybe it's just my lack of knowledge.
So, which ones are the benefits?
The problem with this:
ExampleView X = ExampleView(bloc,...)
It only make sense if you use your bloc as normal class/provider with some Stream\ValueNotifier. Otherwise it has some issues.
If it's global bloc, it's more exhausting way to pass it. You should use XBlocProvider on top of MaterialApp.
By the way, if it's global/top level bloc, you can do this:
XBlocProvider(
bloc: xBloc, // Singleton
child XPage,
...
This way, you can access this bloc from anywhere of the application and also you can listen it.
If it's local bloc, because the way we listen Bloc or ChangeNotifierProvider via InheritedWidget's updateShouldNotify method, it doesn't make sense to pass as constructor because you can't use it directly as you intended. You need to put that instance inside BlocProvider and consume it again, so it's extra work.
https://api.flutter.dev/flutter/widgets/InheritedWidget/updateShouldNotify.html
To overcome multi nested BlocProviders you can use MultiProvider or MultiBlocProvider.
Example:
MultiBlocProvider(
providers: [
XProvider(...),
YProvider(...),
ZProvider(...),
],
child: someWidget,
)
There are multi ways to pass depends on your need but don't worry about InheritedWidget because it's pretty fast and convenient way to obtain your XBlocProvider.
At the end, try to comprehend every approach, I especifically suggest you to grasp this article:
https://www.didierboelens.com/2018/12/reactive-programming---streams---bloc---practical-use-cases/
You'll get the idea when to use bloc with provider or as a singleton or instantiate like your example etc.
The first difference is how you access your bloc from the widget.
when you pass it through the constructor you have direct access to it
when you use BlocProvider then, in most cases depending on your
bloc implementation, you have obtain it via Provider which extends InheritedWidget
using context for example:
final xBloc = Provider.of<XBloc>(context);
What's more when you wrap your bloc with BlocProvider then your bloc's scope is limited to this widget's subtree so it can be only accessed by the BlocProviders descendants.
Not having to pass the bloc in the constructor is the benefit of using the provider. It reduces the complexity of your application by enabling you to propagate information down the widget tree with the help of InheritedWidget. All you just need is to access the bloc from the child widget with BlocProvider.of(context).