whats the difference between these 2 widgets and its necessary to use ChangeNotifier in every Provider or there is many ways to use Provider?
Provider exposes a value down the widget tree so that children can have access to it, regardless their location (but still, they must be at least one level below the provider). Generally you use a provider to expose a "cache" to a series of widgets or as a neat way of sharing data across multiple pages. Note that:
By default, when reading a value stored in a provider, nothing happens. It means that if you're exposing a class and you change some internals of it, children won't listen to it.
If your class exposed via provider mixes with ChangeNotifier then you've the possibility to rebuild listeners when something changes.
You are absolutely not forced to use ChangeNotifier in your model classes exposed by a provider. Use it when you need some widgets to listen to changes but if that's not the case, just ignore it.
Example 1 - (no ChangeNofitier)
You are using a TabBarView to work with tabs (let's say you have 3 pages) and your pages need to share some data. Instead of sharing data using a Navigator, which can become cumbersome, go for a provider.
class MyDataHolder {
int _value = 0;
void updateValue(int a) {
_value = a;
}
}
And then do something like this:
Provider<MyDataHolder>(
create: (_) => MyDataHolder(),
child: MyWidgetWithTabs(),
)
In this way you can easily share data among pages
Example 2 - (with ChangeNotifier)
Still the above case but if you want your pages to listen to changes on a particular data, then use a notifier. Like this:
class MyDataHolder with ChangeNotifier {
int _value = 0;
void updateValue(int a) {
_value = a;
notifyListeners();
}
}
Now children listen to changes. Differently from before, when calling updateValue listeners will be rebuilt.
So both ways are good but they have different purposes. If you don't need a series of listeners to be rebuilt in response to updates, just don't use ChangeNotifier.
Related
I have a screen that displays
a stock value of an asset
an asset selection dropdown
For now, I put all those values in a singe State class:
class AssetsLoaded extends AssetsState {
final List<ActiveSymbol> assets;
List<String> get markets {
return assets.map((e) => e.market).toSet().toList();
}
String selectedMarket;
ActiveSymbol selectedAsset;
int selectedAssetPrice;
AssetsLoaded({this.assets, this.selectedMarket, this.selectedAsset, this.selectedAssetPrice});```
}
Should I separate this State class into several smaller State classes in Cubit architecture? E.g. assets list seem unrelated to selection information. Should I keep all the variables that are consumed by the screen in one state, or should I create several smaller states and cubits?
Generally, you'll want to keep related data together. Since these two bits of information are both related to assets, they should be kept in the same Cubit/Cubit state. This means they can be accessed easily together, alongside if you need to return both at the same time, some version of both, or modify one in relation to another.
If instead you split them up, you may need to introduce loose coupling, which isn't great.
Started recently using the BLoC approach for building apps, and one thing that is not clear is where to "keep" BLoC variables. I guess we can have these two options:
Declare a variable in the BLoC class; for example in my class I can do the following:
class ModulesBloc extends Bloc<ModulesEvent, ModulesState> {
late String myString;
}
And access it in my UI as follows:
BlocProvider.of<ModulesBloc>(context).myString;
Keep it as a state variable; for example I can declare my state class as follows:
class ModulesState extends Equatable {
const ModulesState({required this.myString});
final String myString;
#override
List<Object> get props => [myString];
}
And access it in my UI as follows:
BlocBuilder<ModulesBloc, ModulesState>(
builder: (BuildContext context, ModulesState modulesState) {
modulesState.myString;
}
)
Are there any performance penalties / state stability issues with any of the above approaches?
Thanks!
I am not sure there is an absolute answer but I can at least give my opinion.
In bloc you have 3 objects: bloc, event, state.
The state is the mutable part while the bloc is a description of the your problem (what states to emit for each event). As such, an immutable variable to describe your problem should be, in my opinion, placed inside the bloc. However, anything which might change is the state of your bloc (same as the state of your widget) and should as such be stored in the state.
Example:
You want to create an app where you can set timers. In this app you can have multiple timers, each of which will be identified by a name.
In this case:
your state will be an object containing a double variable called timeCount, which will be incremented each seconds for example.
You bloc will have a final field called name which will have to be set during the creation of the stopwatch.
Interestingly enough, if you want the bloc to also handle the stopwatch creation, you will have 2 states: the first empty, the second with a name and timeCount. See how naturally name became variable and is therefore found in the state now.
I am using bloc library available in Dart to implement "bloc" pattern. I will eventually move onto flutter_bloc library so I can use it inside a real app.
I'm having a bit of difficulty understanding how to create some general blocs that can be called from within more specialized blocs. By specialized I mean some bloc that perhaps manages a specific view. General blocs would then take care of calling APIs or maybe even doing multiple things sequentially.
So my idea is that perhaps I have a StateA that manages certain model and for that reason I use BlocA. Whenever certain event is added to BlocA, I also need to update StateB that is managed by BlocB. I do not want to do this within same bloc because those different states contain different data that might be unrelated. Perhaps I can then have BlocC that is used for specific part of application but certain event should also invoke events and state changes in BlocA and BlocB.
I am considering writing BlocA bloc like this:
class BlocA extends BlocBase<BlocAEvent, BlocAState> {
BlocA(BlocB blocB) : super(BlocAState()) {
_blocB = blocB;
_blocARepository = BlocARepository();
};
BlocARepository _blocARepository;
#override
BlocAState mapEventToState(BlocAEvent event) async* {
if (event is BlocAEventOne) {
yield state.copyWith(valueOne: event.value);
} else if (event is BlocAEventTwo {
// Get data related to BlocAState
final data = await _blocARepository.fetchImportantData()
// ! <-- I also need to fetch additional data but that is managed
// by different bloc - BlocB
_blocB.add(BlocBEventOne(id: data.id));
yield state.copyWith(dataList: SomeModel.fromJSON(data));
}
}
}
and then just creating regular bloc BlocB like this:
class BlocB extends BlocBase<BlocBEvent, BlocBState> {
BlocB() : super(BlocBState()) {
_blocBRepository = BlocBRepository();
};
BlocBRepository _blocBRepository;
#override
BlocBState mapEventToState(BlocBEvent event) async* {
if (event is BlocBEventOne) {
// Get data related to BlocBState
final data = await _blocBRepository.fetchOtherData(event.id)
yield state.copyWith(dataList: SomeOtherModel.fromJSON(data));
}
}
}
I am really unsure if this is correct approach as basically BlocA adds another event. But I am trying to do some reusable blocs and keep data more separate. I know I could also do _blocB.stream.listen in BlocA but that only gives me the state of the BlocB bloc. What I need is the ability to react to certain events.
Do you think that my approach is perhaps convoluted and using BlocObserver would perhaps be more appropriate?
The problem with BlocObserver is that I am unsure how to use it properly from within Flutter app.
First of all, what you are trying to achieve is completely fine, but let's talk about the architecture a little bit.
One option could be that BLoC B subscribes to BLoC A state changes and handles that accordingly. For instance, here a more specific BLoC subscribes to changes of a more generic one: https://github.com/felangel/bloc/blob/08200a6a03e37ce179cef10b65f34ddf6f43f936/examples/flutter_todos/lib/blocs/filtered_todos/filtered_todos_bloc.dart. For that, you would need to adjust the BLoC A state a bit (so you could identify this specific change/event) which could be not a way to go.
Another thing you should consider is whether you want to tightly couple your BLoCs (BLoC B reference is passed to BLoC A via constructor in your example). For that, you can create some kind of mediator class that uses streams inside: BLoC A could publish an event to that mediator class sink and all the subscribers will receive it via exposed streams (in your case - BLoC B should subscribe to that stream) - so the communication there would be like BLoC A -> Mediator -> BLoC B. That's basically what Mediator/Observer OOP design pattern resolves. The advantage of it is that your BLoCs won't know anything about each other, multiple BLoCs could subscribe to multiple different event streams, meaning you could add more subscribers (BLoCs) to your events at any point.
You can define a callback in your event and invoke it in your bloc like this:
blocA.add(BlocAEvent(whenDone: () {
blocB.add(BlocBEvent())
}));
Another way to do this is to listen to events being published by a BloC:
// Inside some widget
this.context.read<MyBloc>().listen((myBlocState) {
if (mounted && myBlocState.isSomeState) {
this.context.read<MyOtherBloc>().add(MyOtherBlocEvent());
}
});
This solution would ensure your BloCs are not coupled and you can chain the publishing of your events based on the state of another BloC.
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 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>();