'emit was called after an event handler completed normally' issue inside a periodic Timer

319

Your code does not wait for the callback inside Timer.periodic to complete - the _onBeehivesLoaded method finishes executing, hence when the callback tries to emit a new state (BeehivesLoadedSuccessfully), you get this error.

To resolve this, instead of emitting a new state inside the callback, you should add a new event to the BLoC and handle it later as any other BLoC event.

  1. First of all, create a new event, like HomeBeehivesLoaded:
class HomeBeehivesLoaded extends HomeBeehivesEvent {
  final List<Beehive> beehives; // <-- Not sure if Beehive is the right type, just an assumption

  const HomeBeehivesLoaded(this.beehives);
}
  1. Register a new event handler in your BLoC that will update the state based on the loaded behives:
class HomeBeehivesBloc extends Bloc<HomeBeehivesEvent, HomeBeehivesState> {
  HomeBeehivesBloc() : super(BeehivesInitial()) {
    <...>
    on<HomeBeehivesLoaded>(_onHomeBeehivesLoaded);
  }
  
  void _onHomeBeehivesLoaded(HomeBeehivesLoaded event, Emitter<HomeBeehivesState> emit) {
    emit(BeehivesLoadedSuccessfully(beehives: event.beehives));
  }

  <...>
}
  1. Inside the Timer.periodic callback, add this event once you get the response from the repository instead of emitting a state:
class HomeBeehivesBloc extends Bloc<HomeBeehivesEvent, HomeBeehivesState> {
  <...>

  Future<void> _onBeehivesLoaded(Emitter<HomeBeehivesState> emit) async {
    <...>

    Timer.periodic(duration, (timer) async {
      try {
        await repository.getHomeBeehives().then((beehives) async {
          add(HomeBeehivesLoaded(beehives: beehives));
        });
      } catch (exception) {
        log(exception.toString());
      }
    });
  }
}
Share:
319
RedZ
Author by

RedZ

Updated on January 04, 2023

Comments

  • RedZ
    RedZ over 1 year

    Im using Flutter and flutter_bloc to make an app where it periodically sends an API request to my server and requests data. It was working perfectly at first before i implemented the periodic functionality using Timer, this is my bloc file:

    class HomeBeehivesBloc extends Bloc<HomeBeehivesEvent, HomeBeehivesState> {
      HomeBeehivesBloc() : super(BeehivesInitial()) {
        on<LoadBeehives>((event, emit) => _onBeehivesLoaded(emit));
      }
    
      Future<void> _onBeehivesLoaded(Emitter<HomeBeehivesState> emit) async {
        emit(BeehivesLoading());
    
        final repository = BeehivesRepository();
    
        const duration = Duration(seconds: 5);
        Timer.periodic(duration, (timer) async {
          try {
            await repository.getHomeBeehives().then((beehives) async {
              emit(BeehivesLoadedSuccessfully(beehives: beehives));
            });
          } catch (exception) {
            log(exception.toString());
          }
        });
      }
    }
    

    But im getting this exception:

    'package:bloc/src/emitter.dart': Failed assertion: line 114 pos 7: '!_isCompleted': 
    
          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>((event, emit) {
              future.whenComplete(() => emit(...));
            });
          
            **GOOD**
            on<Event>((event, emit) async {
              await future.whenComplete(() => emit(...));
            });
    

    Ive tried searching everywhere for a solution, but honestly this is the first time i used the new version of the bloc package (never used emit before), and i would like some suggestion as to how to solve this issue.

    Side question: Is it a good idea to implement the periodic timer there ? because i have seen some people implement it within the frontend (Stateful widget for example), i would also love any suggestions about this.

    Thank you very much!

  • RedZ
    RedZ about 2 years
    Yes i was using this trick a while back, but i guessed it wasnt the best way to do it so i switched to using emit on the new version of BLoC and havent ran into any trouble except when i tried using Timer, but ill use the event add trick again, thank you very much for your help