I'm working with Flutter quite a long time and have a bunch of released products. I never really liked BLoC and preferred to use Provider or later Riverpod.
I just don't understand that event concept. Why do we still need it? And i'm confused because of it's actual popularity... BLoC has Cubit subclass that seems to be more simple to use, but everyone just keep telling: "Cubit is simpler, but not so functional". But what are limitations?
I even think that Cubits are MORE USEFUL and MORE SIMPLE at the same time:
With Cubit you just call it's method with params. You still can listen its state AND get the method return value if needed too.
You don't need extra coding implementing these Event Types.
You don't need extra extra coding implementing how bloc will handle every single event type. METHODS DO THAT JUST FINE.
example:
User taps some product's "Add to cart" button.
Cubit:
cartCubit.addProduct(productId);
BLoC:
cartBloc.addEvent(UserAddsProductEvent(productId));
inside them:
Cubit:
void addProduct(String productId) async {
//some validation...
if(...){...}
final result = await cartRepo.addProduct(id);
if(result == ...) {
state = someState;
//....
}
BloC:
void addEvent(CartEvent event) {
if (event is UserAddsProductEvent) {
_addProduct(event.productId)
} else if (event is....) {
//.....
}
}
void _addProduct(String productId) async {
//some validation...
if(...){...}
final result = await cartRepo.addProduct(id);
if(result == ...) {
state = someState;
//....
}
What is the point?
There's a good overview of Cubit vs Bloc in the official documentation.
In short, Cubit's advantage is simplicity, while Bloc provides better traceability and advanced ReactiveX operations.
In our projects, we use both Cubit for simpler cases, and Bloc if the logic is more complicated and some "limitations" actually become useful:
You can emit a new state only as a reaction to an event, so the implementation is more straightforward (but more verbose as well).
All the events are processed one by one. Again, it makes implementation more reliable and easier to maintain.
Also, that can be a matter of personal preference, but I like Bloc for its close mapping to the FSM pattern. In most cases, the application state can be nicely represented as a state machine. It's easier to discuss the implementation even with a whiteboard, as you can just show the scheme with a number of states and events changing that state.
If you're happy with Cubit, then you probably don't need Bloc. After all, the main goal is to make the architecture easy to understand and to maintain.
BLoC
The advantage of having events instead of direct method calls is that you can debounce/throttle, buffer the stream before doing your logic.
In other words, you can use special methods applicable for events logic.
Cubit
If you start with Cubit for a new project, the idea why it exists is that later you will have the ability to migrate from Cubit to BLoC.
This means that if at the beginning of a project you think that BLoC is too much overhead and you need simpler state management (without events, boilerplate, etc.) you can choose cubit and migrate to BLoC with fewer efforts, than if you selected a different state management solution like MobX or Riverpod.
So with Cubit, you first implement the state and functions. Later if you decide to switch to BLoC you add events and EventHandler.
More you can read here (official docs): Cubit vs BLoC
Cubits are easy to understand and implement, while still being quite powerful. I wrote a few small to middle sized apps using cubits and hooks, so it's a matter of preference whether one chooses Blocs or Cubits or both.
You only need to be careful with the things that you emit in state, many times I saw developers (me included ;) being surprised that UI doesn't update. It was because the object/list was equal to the previous one, either becase equalTo() wasn't correctly implemented or the list size remained the same.
I actually wrote an extensive Flutter tutorial showing how to write a clean Flutter app with cubits and hooks.
Related
I'm new to using the Bloc pattern in Flutter, and I wish to update some widgets (i.e. state) for every bloc concerned by an external change.
For example, imagine that I have a trading application, and so I have a "Trading" component that receive some information updates.
My "Trading" component receive new information, but then how does it transmit the new information to the Bloc and so, how it emits (and update) the widget.
Any idea how I can do this? What is the correct pattern to follow ?
You need to emit the state changes (your trading data here) to your state using a BLoC / Cubit which will then rebuild the UI using BlocBuilder.
I know StateProvider is used for immutable states like counter value.But I couldn't understand StateNotifierProvider.Why and How I should StateNotifierProvider in my apps?
StateNotifierProvider is a good interface when a single state wants to be consumed for changes, and updated via method calls on its underlying notifier, encouraged to treat state as an immutable.
If your problem fits that, this provider is the best.
Some patterns call for a bit less rigor in updating the state, and for those a StateProvider can provide a read/write variable that still has notification wiring and the possibility of consuming other providers to give its value. A StateProvider still has an underlying notifier, but this notifier isn't subclassed with additional behavior as a StateNotifier would have been, enroute to the StateNotifierProvider.
If suddenly your state becomes more complex than true and false, you should use StateNotifierProvider. You can put methods in the StateNotifier class to handle your state. For example, adding or removing items from your state-List are two separate methods that you can no longer implement in StateProvider
What are the actual advantage(s) of Bloc over Cubit?
In addition to traceability (which you can also achieve with appropriate logging in Cubit), and advanced event transformations (I can't think of any "advanced" event transformations that Cubit can't do, since there is always a way to do it with Cubit. And if you're using clean architecture, domain/data layer can help with complex data manipulations).
Sharing and sourcing events
These are the things that I'm looking for that should be able to do with Bloc since these things can't be actually done with Cubit. However, it appears that these are impossible (or is it?) because adding event on a Bloc requires you to identify the actual Bloc where the event will be added. bloc.add(YourEvent()).
Also, event sharing is somewhat debatable because this can lead to a bad architecture/hard to maintain.
For event sourcing, I can't find in the docs if this is possible (reversing back to a specific past state?).
Am I missing something here?
As far as I know reversing to past state can be easily done when you have immutable states regardless of whether it is bloc or cubit. Having immutable states allows you to store list of states and restore whenever you need a specific state.
Bloc has no advantages over cubit but rather different purpose. In cubit you have action=>response (function=>states) whereas in bloc you have streams.
What cubit cannot do?
For example you can have two events being processed concurrently when using bloc (since bloc 7.20) but you cannot call two functions simultaneously on cubit.
Sharing events
You can share events implementation between different blocs because you have to specify what events bloc implements.
class MyBlocA extends Bloc<MyEvents, StatesA>
class MyBlocB extends Bloc<MyEvents, StatesB>
If I understood correctly, what you want to do is to process a single event in two different blocs, which you cannot do because event is emitted to a specific bloc. So it requires two calls:
blocA.add(EventA);
blocB.add(EventA);
Depending on your case you might listen to state of MyBlocA inside MyBlocB. This way whenever event for MyblocB appears, the action would depend on the state of MyBlocA.
Blocs and Cubits are same in terms of state management with only one difference of state mutation: Bloc is event driven and Cubit is method driven.
Apart from all of this, in terms of architecture not much is different as of now. All others have been mentioned precisely by #chris in the above. It is up to the developer on how to maintain the state in a way is manageable for us.
aren't events for dependency injection?
Cubit
call Action
set State
Bloc
trigger Event (inject something, depending on screen/page it was trigger from. as Example)
then call Action
and set State
I have an application where I display a list of cards that represent a model (Let's say a Person). These are stateless widgets as they cannot be changed directly. These instances are obtained from an API and, since the amount of items is large, I am using AutomaticKeepAliveClientMixin to keep them from reloading and triggering more API calls.
When clicked, these cards lead to the Person's page where there we can edit some information on them. Using a StatefullWidget here with setState takes care of all issues related to the page. However, when I pop the individual page the card that was clicked does not reflect the changes made.
I was thinking of writing a ChangeNotifier wrapper for each Person but from what I've read this is a bad idea. Using more powerful state management tools like BLoC or Redux seems like overkill too. How can I implement this properly?
I think you should try:
ontap: () async {
await Navigator.push ....);
ref
}
I have started studying Flutter around a month ago, and I have read and watched some good tutorials, the best of them being a responsive architecture with Provider and view models to prevent code repetition when creating apps.
Now I am writing my very first app for my work and I have some API calls to do. I haven't found a good article or video yet teaching a good way to organize my API requests into separate files properly to prevent repetition, so I come here to ask for your guidance regarding this.
Here is how I did it. I thought about creating an api.dart service that implements the methods of http package. So far it's only get(), but I can already see the others will end up repeating the condition too much.
class Api {
Future<dynamic> get(String url) async {
final response = await http.get('$kBaseUrl/$url');
if (response.statusCode != 200) {
throw Exception(json.decode(response.body)[0]['mensagem']);
}
return json.decode(response.body);
}
}
Then, in the only view model I have so far, I implement the get() from the api.dart on a button click, for example. The model within the button is how the architecture I mentioned above is done.
Future<void> submit() async {
print('Email: $email, password: $password');
get();
}
Future get() async {
_setBusy(true); // loading status on
_setError(''); // clean errors
try {
await _api.get('clientes');
} catch (e) {
_setError(e.message);
}
_setBusy(false); // loading status off
}
PrimaryButton(
onTap: () {
model.submit();
},
disabled: model.busy, // the loading state to modify the styling of the button
),
And this is it. I have the feeling it could be much better, and I would prefer to have the best way possible from the beginning so I can learn more about structuring my files and also keeping it clean as the app grows. I will appreciate any guidance, code, article, video and/or repository.
Great question, #gamofe.
As far as file structure goes, Flutter is still a bit of the wild west. That being said, in my projects, and in the large majority of tutorials I've read, folder structure looks like this:
lib/src
lib/src/repositories
lib/src/repositories/common
lib/src/repositories/user
lib/src/repositories/user/user_repository.dart
lib/src/repositories/item/item_repository.dart
lib/src/blocs
lib/src/blocs/user
lib/src/blocs/user/barrel.dart
lib/src/blocs/user/user_state.dart
lib/src/blocs/user/user_events.dart
lib/src/blocs/user/user_bloc.dart
lib/src/models/user.dart
lib/src/screens/login_screen.dart
lib/src/screens/item_feed.dart
lib/src/widgets
Additionally, I would say it is exceptionally rare that you should be making an API call directly from your view layer. I would recommend reading up on the BLoC pattern. It is the commonly accepted (and recommended by Google) method of managing state in Flutter apps.
Basically, a BLoC is a stream. In your view, you listen for new state events from the stream. If you want to update data, you "dispatch" a new event to the stream, which is ultimately transformed into a state output.
Implementing the Bloc pattern in your application will help you achieve the separation and DRY code you are looking for.
Flutter Bloc is a helper library that will get you up and running with the Bloc pattern. The documentation is quite good, and the examples are numerous. There are docs that give overviews of how to manage the relationship between views, state, and network requests quite well.
FWIW, I've put together an example Flutter application that implements all these concepts. I use copy it for each new project to help me get rocking and rolling fast. Feel free to poke around. I've done my best to follow best practices, so it should hopefully serve as a decent model.