Bloc: How to dispatch an event many times

Created on 24 Mar 2019  ·  40Comments  ·  Source: felangel/bloc

Hi there, first of all let me thank you for the awesome libraby.
I face a problem with my block. I cannot send an event multiple times. It seems that mapEventToState is an asynchronous function, so currently can be equal to the state, so the data cannot be returned.
You can give me a solution.
Thanks very much. Sorry, my english is bad.

question

Most helpful comment

@felangel Thank you very much, you are my life

All 40 comments

I am trying to call many times to retrieve data from network

Hi @hahai96 👋

Thanks for the positive feedback! Can you provide any code to show what you’re tying to accomplish? I’m not sure I understand the problem. Thanks! 👍

if (event is ResendMessageEvent) {
      yield ChatInitial();

      var service = await _networkFactory.apiService();
      BaseCallBack baseCallBack = BaseCallBack();
      ChatState state = await baseCallBack
          .getDataFromApi(
              context,
              _networkFactory,
              service.sendMessage(
                  event.fbPageId,
                  event.threadId,
                  SendMessageRequest(
                          data: MessageData(
                          message: event.msg,
                          tempId: event.threadId + '${event.timeMsg}'))
                      .toJson()))
          .map((data) => _handleReSendMessage(data, event))
          .first;
      yield state;
}
ChatState _handleReSendMessage(data, ResendMessageEvent event) {
    if (data is String) {
      return SendMessageFail(error: data, timeMsg: event.timeMsg);
    } else {
      SendMessageResponseEntity response =
      SendMessageResponseEntity.fromJson(data);
      if (response != null) {
        MessagesResponse messageResponse = response.data;

        return ResendMessageSuccess(
            data: messageResponse, timeMessage: event.timeMsg);
      } else {
        return GetMessageFail(error: "Send messages fail");
      }
    }
  }

Sorry, I can't format the code.
Thank you very much.

No problem! So the problem you’re having is you dispatch ResendMessageEvent and it doesn’t re render the ui?

yes, I discovered currentstate = state, therefore data is not returned

Can you show me the code for the ChatState class?

//file state
abstract class ChatState extends Equatable {
  ChatState([List props = const []]) : super(props);
}

class ResendMessageSuccess extends ChatState {
  MessagesResponse data;
  int timeMessage;

  ResendMessageSuccess({this.data, this.timeMessage});

  @override
  String toString() =>
      'ReSendMessageSuccess {${data.toJson()} - time $timeMessage}';
}

I think I see the problem. You’re using Equatable which overrides the equality operator and makes it possible to compare different instances of the same object however you aren’t passing the props to its superclass.

Try updating your constructor like this:
ResendMessageSuccess({this.data, this.timeMessage}) : super([data, timeMessage]);

Let me know if that helps.

It does not work for me. It only works if I send data one at a time

Are you able to share a link to the full code so I can run it locally and help debug it?

What do you mean by send one data at a time?

I have a button, and I click it many times, it will send data only 1 time

Can you share a link to the repo so I can run your app and see what’s wrong?

This project is private, but I will send you a demo in the next few minutes

Sounds good!

@felangel https://github.com/hahai96/flutter_app_demo
Please clone it to your computer and notify me, I will delete it

@hahai96 thanks! I just cloned it 👍

@hahai96 so what's the problem you're having? I loaded the app and when I click the button I see the number of successful sent messages goes up every time

Please press it repeatedly

@felangel it only increased by 1 unit even though you pressed it many times

@hahai96 that's because you're always yielding ChatInitial() in your bloc before you do the async work so BlocBuilder doesn't increment the count.

If you remove the yield ChatInitial(); from your ChatBloc it will work as you expect.

Also, you need to always pass the props to the superclass when you are using Equatable.

chat_bloc.dart
chat_event.dart
chat_state.dart

Hope that helps!

@felangel Thank you very much, you are my life

@felangel Blocs will ignore duplicate states. If a Bloc yields State state where currentState == state, then no transition will occur and no change will be made to the Stream.

currentState == state, they compare values ​​in state or not?

Bloc just checks if currentState == the yielded state.

The default dart behavior is to return true only if currentState and yielded state are the same instance.

With Equatable you override == and return true if the two objects have the same properties.

{ currentState: SendMessageSuccess, event: SendMessageEvent, nextState: SendMessageSuccess }

currentState == nextState but it still works, please explain help me.

The messages in each SendMessageSuccess are different.

@felangel I don't know how to thank you ❤️❤️❤️

Glad I could help! 👍

@felangel Why remove chatInitState it works again. I usually set "yield loading()" before an asynchronous job, but it still works for me. Is there any reason here?

@hahai96 it's because you were only incrementing the counter on a success state and when you tap the button repeatedly quickly then what happens is you set the state to initial state first then as soon as the async action completes and you yield the success state immediately the state becomes initial state again before the UI rebuilds.

@felangel Great, thank you very much.

@felangel Sorry for bothering you.

if (event is ResendMessageEvent) {

      // I want to display data temporarily before the api returns
      yield ShowTemporaryData();

      var service = await _networkFactory.apiService();
      BaseCallBack baseCallBack = BaseCallBack();
      ChatState state = await baseCallBack
          .getDataFromApi(
              context,
              _networkFactory,
              service.sendMessage(
                  event.fbPageId,
                  event.threadId,
                  SendMessageRequest(
                          data: MessageData(
                          message: event.msg,
                          tempId: event.threadId + '${event.timeMsg}'))
                      .toJson()))
          .map((data) => _handleReSendMessage(data, event))
          .first;
      yield state;
}

how the above code can work. Do you have any solution? please help me

What's the problem?

The problem of @hahai96 .
If I don't remove the yield chatInitState, is there any way it still works?

Suppose, I want to display temporary data before the api returns. I will call this event many times, so it will not work for me.

The above code should work. What's the problem you're having?

@hahai96 it's because you were only incrementing the counter on a success state and when you tap the button repeatedly quickly then what happens is you set the state to initial state first then as soon as the async action completes and you yield the success state immediately the state becomes initial state again before the UI rebuilds.

@felangel Sorry, I'm too busy. If I don't remove "ChatInitial()", is there any way it still works?

It works even with ChatInitial the problem is the state change happens so quickly when you spam the button. You might consider disabling the button while a request is in progress.

Was this page helpful?
0 / 5 - 0 ratings