Bloc: Transform Events Based on Type

Created on 18 Sep 2019  路  13Comments  路  Source: felangel/bloc

How do you transform events only if they match a specific type in transformEvents?
Example:
In mapEventToState, you can map to different states based on the event type. Here, there is PostGetData, PostRefresh, and PostLike.

  @override
  Stream<PostState> mapEventToState(PostEvent event) async* {
    if (event is PostGetData) {
      yield PostThreadLoading();
      yield* postThreadRefresh();
    }
    if (event is PostRefresh) {
      yield* postRefresh();
    }
    if (event is PostLike) {
      StatusResponse statusResponse =
          await networkProvider.postPostLike(postId, event.like);
      yield PostRefreshing(postThread: postThread);
      dispatch(PostRefresh());
    }
  }

I need to debounce the PostLike event to prevent the user from hitting the like button too many times and spamming the server.
In transformEvents, you can debounce all events as follows:

  @override
  Stream<PostState> transformEvents(
    Stream<PostEvent> events,
    Stream<PostState> Function(PostEvent event) next,
  ) {
      return super.transformEvents(
        (events as Observable<PostEvent>).debounceTime(
          Duration(milliseconds: 500),
        ),
        next,
      );
    }
 }

How can I debounce only PostLike events?
Thanks!

question

Most helpful comment

Hi,

I just updated @craiglabenz great answer to match the latest bloc version (4.0.0):

  @override
  Stream<Transition<Event, State>> transformEvents(
    Stream<Event> events,
    TransitionFunction<Event, State> transitionFn,
  ) {
    final nonDebounceStream =
        events.where((event) => event is! PostRefresh);

    final debounceStream = events
        .where((event) => event is PostRefresh)
        .debounceTime(Duration(milliseconds: 300));

    return super.transformEvents(
        MergeStream([nonDebounceStream, debounceStream]), transitionFn);
  }

Thanks again for this wonderful library!

All 13 comments

Thanks @AhmedMozaly for taking a look. The problem with whereType and filterBy is that it removes events that I still need.
My goal is to reach into the stream and only filterBy the specific event I want to debounce. Is that possible with a single stream?

Hi @searchy2 馃憢
Thanks for opening an issue!

Have you tried something like:

@override
Stream<CounterState> transformEvents(
  Stream<PostEvent> events,
  Stream<PostState> Function(PostEvent event) next,
) {
    final observableStream = events as Observable<PostEvent>;
    final nonDebounceStream =
      observableStream.where((event) => event is! PostRefresh);
    final debounceStream = observableStream
      .where((event) => event is PostRefresh)
      .debounce(Duration(milliseconds: 300));
    return super.transform(Observable.merge([nonDebounceStream, debounceStream]), next);
}

Hope that helps 馃憤

Thanks for your help! It seems I do need two streams; an extra one for the type I need to filter.

For anyone else who comes here in a post-extension methods world, this is the new equivalent:

@override
Stream<CounterState> transformEvents(
  Stream<PostEvent> events,
  Stream<PostState> Function(PostEvent event) next,
) {
    final nonDebounceStream =
      events.where((event) => event is! PostRefresh);
    final debounceStream = events
      .where((event) => event is PostRefresh)
      .debounceTime(Duration(milliseconds: 300));
    return super.transformEvents(MergeStream([nonDebounceStream, debounceStream]), next);
}

A few updates in trying to implement debounce:

  • debounce is not part of the default Stream methods. Instead, it can be added with https://pub.dev/packages/stream_transform
  • debounced events do not emit until the delay for the last event has been met. This is not the desired behavior as I would like each event to be emitted every x seconds, not until x seconds have elapsed since the last event. The problem is that the events keep coming so an event is never emitted because the delay is not met.

@searchy2 debounceTime is part of rxdart which migrated to extensions so you don't need to use stream_transform.

The behavior you're describing doesn't sound like debounce. You might want to take a look at the rxdart transformers and see what meets your requirements (maybe throttle?). Ultimately, you have access to the Stream and can manipulate it however you'd like. Hope that helps!

Thanks for pointing me in the right direction @felangel

I wish you a great day and look forward to meeting you at a Flutter meetup some day!

Hi,

I just updated @craiglabenz great answer to match the latest bloc version (4.0.0):

  @override
  Stream<Transition<Event, State>> transformEvents(
    Stream<Event> events,
    TransitionFunction<Event, State> transitionFn,
  ) {
    final nonDebounceStream =
        events.where((event) => event is! PostRefresh);

    final debounceStream = events
        .where((event) => event is PostRefresh)
        .debounceTime(Duration(milliseconds: 300));

    return super.transformEvents(
        MergeStream([nonDebounceStream, debounceStream]), transitionFn);
  }

Thanks again for this wonderful library!

Hi,

I'm beginner, i'm getting issue with this

   @override
   Stream<LoginState> transformEvents(
    Stream<LoginEvent> events,
   Stream<LoginState> Function(LoginEvent event) next,
   ) {
   final nonDebounceStream = events.where((event) {
  return (event is! EmailChanged || event is! PasswordChanged);
  });

final debounceStream = events.where((event) {
  return (event is EmailChanged || event is PasswordChanged);
}).debounceTime(Duration(milliseconds: 300));

return super.transformEvents(
  nonDebounceStream.mergeWith([debounceStream]),
  next,
);

}

@felangel i tried to solve but not yet solved, can you please help me!
Thank You

@venkateshvk7 check out the comment above by @synapticvoid for the updated syntax 馃憤

I am well pleased with flutter_bloc. Thank you sir!

For anyone else who comes here in a post-extension methods world, this is the new equivalent:

@override
Stream<CounterState> transformEvents(
  Stream<PostEvent> events,
  Stream<PostState> Function(PostEvent event) next,
) {
    final nonDebounceStream =
      events.where((event) => event is! PostRefresh);
    final debounceStream = events
      .where((event) => event is PostRefresh)
      .debounceTime(Duration(milliseconds: 300));
    return super.transformEvents(MergeStream([nonDebounceStream, debounceStream]), next);
}

Hi

Hi @searchy2 馃憢
Thanks for opening an issue!

Have you tried something like:

@override
Stream<CounterState> transformEvents(
  Stream<PostEvent> events,
  Stream<PostState> Function(PostEvent event) next,
) {
    final observableStream = events as Observable<PostEvent>;
    final nonDebounceStream =
      observableStream.where((event) => event is! PostRefresh);
    final debounceStream = observableStream
      .where((event) => event is PostRefresh)
      .debounce(Duration(milliseconds: 300));
    return super.transform(Observable.merge([nonDebounceStream, debounceStream]), next);
}

Hope that helps 馃憤

Hi @felangel , @craiglabenz and @synapticvoid I have page with products where user can add to wishlist or remove them and I wanted to not allow the user to click add to wishlist button while I am processing one So I got this post and tried to use all the three examples you gave here guys and it is giving me errors I am using bloc 6.0.0 I want to add debounce time to add and remove wishlist events and all other events as normal so pls give me example for my version thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tigranhov picture tigranhov  路  3Comments

ricktotec picture ricktotec  路  3Comments

rsnider19 picture rsnider19  路  3Comments

krusek picture krusek  路  3Comments

nerder picture nerder  路  3Comments