Flutter Bloc Error : emit was called after an event handler completed normally - between two functions

461
void _onChangeLanguage(
    ChangeLanguageEvent event,
    Emitter<StateA> emit, {
    bool isFromLogin = false,
  }) async

This should be a major red flag. A call marked as async, but not returning a Future<>. There is no way, the caller could possibly await this call. Or even know that they should await this call.

Make it return a proper Future<void> instead of just void and your bloc should pick up on that and properly await the call.

There even is a linter rule for this: avoid_void_async. Did you turn off your linter? Don't do that. Turn your linter on and listen to it. Your other function has the same problem.

Share:
461
Daniel Roldán
Author by

Daniel Roldán

Joven amante del desarrollo movil que busca sobrellevar su experiencia en grande

Updated on December 01, 2022

Comments

  • Daniel Roldán
    Daniel Roldán over 1 year

    I have the following problem...

    emit was called after an event handler completed normally. This is usually due to an unawaited future in an event handler. Please make sure to await all asynchronous operations with event handlers and use emit.isDone after asynchronous operations before calling emit() to ensure the event handler has not completed.

    BAD on((event, emit) { future.whenComplete(() => emit(...)); });

    GOOD on((event, emit) async { await future.whenComplete(() => emit(...)); }); )

    What happens is that in a function called _onLogIn, if the user has changed the language, it goes from there to another function inside the bloc, these two functions do not depend on each other, I mean that each function is called in different pages of the application, but still _onLogIn checks the _onChangeLanguage function.

      UserBloc({this.usecases}) : super(UserInitial()) {
        on<LogInEvent>(_onLogIn);
        on<ChangeLanguageEvent>(_onChangeLanguage);
      }
    

    _onLogIn function :

    void _onLogIn(
        LogInEvent event,
        Emitter<StateA> emit,
      ) async {
    
        emit(UserLoading());
    
        final userOrFailure = await services.logIn(
          x: event.x,
          y: event.y,
        );
    
        await userOrFailure.fold((user) async {
    
            /// If the user is logging in for the first time and does not
            /// have a preferred language.
            if (user.preferredLanguage == null) {
              emit(UserSuccess());
    
              emit(UserAlreadyLogged(connectedUser));
    
            } else {
              /// An ChangeLanguageEvent object
              ChangeLanguageEvent event = ChangeLanguageEvent(
                user.preferredLanguage,
                user.someId,
              );
    
              /// Call the other function in the same bloc
              this._onChangeLanguage(
                event,
                emit,
                isFromLogin: true,
              );
            }
          
        }, (failure) {
          emit(UserError(failure.message));
        });
      }
    

    _onChangeLanguage function :

      void _onChangeLanguage(
        ChangeLanguageEvent event,
        Emitter<StateA> emit, {
        bool isFromLogin = false,
      }) async {
    
        final successOrFailure = await services.updateLanguage(
          event.language,
          event.someId,
        );
    
        await successOrFailure.fold( // ! HERE THE ERROR WHEN I LOG IN; but when i changed the language from the application i don't have an error
          (language) async {
    
            emit(ChangeAppLanguage(language));
    
            final sessionOrFailure = await services.getSession();
    
            sessionOrFailure.fold(
              (session) {
                /// I need this condition to know if the language comes from login
                if (isFromLogin) {
                  emit(UserSuccess());
                }
                emit(UserAlreadyLogged(session));
              },
              (failure) => emit(UserError(failure.message)),
            );
          },
          (failure) {
            emit(UserError(failure.message));
          },
        );
      }
    

    Any idea why? Thank you

    • pskink
      pskink about 2 years
      i assume that _onLogIn is called inside some on<...> method, right? if so it seems that your emit is called after on method finished - most likely this is because your fold methods are not awaiting their async callbacks
    • Daniel Roldán
      Daniel Roldán about 2 years
      @pskink Yeah yeah, both, i add it in the description And have you an idea how can i fixed it ?
  • Daniel Roldán
    Daniel Roldán about 2 years
    That helps me a lot, thanks! Put "await" before calling the other function too.