I have an uninitialized Timer object in my class and when user hits start button, it is initialized, and inside it's callback I need to call yield ...
.
I'm new to dart and this is the code I have so far. Where should I modify?
if (event is CounterETimerStart) {
timer = Timer.periodic(Duration(seconds: 1), (timer) async* {
yield CounterNewSecond(++m.passedTime);
});
}
Hi @SIMMORSAL 👋
You can add an event and react to it.
if (event is CounterETimerStart) {
timer = Timer.periodic(Duration(seconds: 1), (timer) {
add(TimerTicked(payload));
});
}
if (event is TimerTicked) {
yield CounterNewSecond(event.payload);
}
Hey @RollyPeres,
Thanks. This is very good, but it just doesn't feel that elegant!
Is it the only way to do this?
I assume you're after something like
yield* Timer.periodic(Duration(seconds: 1), (timer) async* {
yield something;
});
but that's not possible.
Alternatively you could use:
Stream<int> get stream async* {
yield* Stream.periodic(const Duration(seconds: 1), (index) => index);
}
stream = Stream.periodic(const Duration(seconds: 1),
(index) => CounterNewSecond(++m.passedTime));
yield* stream;
I wrote this and now CounterNewSecond
is yielding, but the bloc now can't listen to any event. I believe I've not written the code correctly.
Could you provide a more complete code, because I don't really know what to do with the code in the alternate way you offered.
If you want the bloc to react to a specific event then you need to add the event to the bloc like:
add(TimerTicked(payload));
If you want to directly yield a state then the alternative suggestion would work.
stream = Stream.periodic(const Duration(seconds: 1), (index) => CounterNewSecond(++m.passedTime)); yield* stream;
This assumes CounterNewSecond
is a state class and not an event.
You can't yield events from the bloc but only states. Events are added to the bloc in order to be transformed into states.
I realize that. What I'm saying is when I'm listening to start
event, I want to keep yielding the CounterNewSecond
state, and the code above does that perfectly.
The problem with it is that when the above code starts running, the bloc wont receive any other events that I send to it from the screen code.
You're adding an event per second, there's plenty of time for other events to be processed. The events are processed in the same order they are added, e.g.: CounterNewSecond
, SomeEventFromUI
, CounterNewSecond
.
If you think there's some issues you can't overcome feel free to share a minimal reproduction gist/repo and I can have a look 👍
Imagine this;
There are 3 buttons on screen, btn1
, btn2
, and btnStart
.
The bloc code looks like this:
counter_event.dart:
class CounterEvent {}
class EventBtn1Click extends CounterEvent {}
class EventBtn2Click extends CounterEvent {}
class EventBtnStartClick extends CounterEvent {}
counter_state.dart:
class CounterState {}
class StateOnBtn1Click extends CounterState {}
class StateOnBtn2Click extends CounterState {}
class StateOnBtnStartClick extends CounterState {
final int s;
StateOnBtnStartClick(this.s);
}
counter_bloc.dart:
class CounterBloc extends Bloc<CounterEvent, CounterState> {
final m = ... // a model
@override
Stream<CounterState> mapEventToState(
CounterEvent event,
) async* {
print("--- " + event.toString());
if (event is EventBtn1Click) {
yield StateOnBtn1Click();
}
else if (event is EventBtn2Click) {
yield StateOnBtn2Click();
}
else if (event is EventBtnStartClick) {
yield* Stream.periodic(const Duration(seconds: 1),
(index) => StateOnBtnStartClick(++m.passedTime));
}
}
Somewhere in my UI code, I print all of the states
that I receive with this code:
print("+++ " + state.toString());
Now if I click on btn1, then btn2, then btnStart and then repeat and start from btn1..., this is the output I get:
--- EventBtn1Click
+++ StateOnBtn1Click
--- EventBtn2Click
+++ StateOnBtn2Click
--- EventBtnStartClick
+++ StateOnBtnStartClick
+++ StateOnBtnStartClick
+++ StateOnBtnStartClick
+++ StateOnBtnStartClick
+++ StateOnBtnStartClick
...
It seems like when Stream.periodic(...)
starts working, the thread is occupied by it and the bloc wont react to any other event coming in.
Your model m
should not be on the bloc but on the state itself.
Here's a gist which does what I assume you want.
My bad, the approach was not working because the default bloc event processing operator asyncExpand
pauses the event stream in order to process it, event by event and Stream.periodic
never completes so other events won't get the chance of being processed, unless for some rx magic. switchMap
converts each incoming event into a new Stream
that will complete after being processed so now other events will get the chance of being handled.
Hope that solves your issue and sorry for missing this crucial aspect ✌
No problem. Sorry for late replies.
About the model not being on the bloc, why? If bloc is supposed to house all the business logic, and the logic does some work on the model and modifies it and later saves it, wouldn't the best place for it be the bloc? As I understand and read about it, bloc pattern is like MVVM in native android. The ViewModel class holds all the data, and it's then passed down to View so it can show stuff based on it, and sometimes directly access the data through the viewModel
object, but the view never directly holds it or does operations on it. Have I understood the BLoC pattern wrong?
And thanks for the explanation, but could I trouble you to use the switchMap
in a code snippet? I'm not well versed enough in rx yet!
There's similarities between the two patterns but also differences.
While in MVVM you'd do bindings on view model's props which holds the data, with bloc you'd consume state props since bloc's state is in charge of holding the data and not the bloc itself.
To have a better understand please check out the architecture overview
Here's a gist which does what I assume you want.
My previous reply already contains a link to a complete rework of your code to use switchMap
so if you click on the blue 'gist' you'll get to it. 👍
Totally overlooked the gist link. Thanks for both links, much appreciated.
Most helpful comment
Totally overlooked the gist link. Thanks for both links, much appreciated.