Search code examples
androidfluttertimeoutflutter-blocdio

Flutter BloC repeat an event again after dio request time out


I have a problem on my flutter app, when try to load a data from api using dio and this api is private so need to be connected to the same network, so to check everything is fine I tried to connect using mobile data that means dio connection won't success and return timeout, when I call that inside BLoC and use BloCBuilder to build UI depending on state bloc return loadingstate then return errorstate and try to do the event again and fail then repeat this over and over, I just want to avoid this and return error state only and stop listening on that event

void _loadAllSpecialities(
      LoadAllSpecialities event, Emitter<DoctorsState> emit) async {
    emit(
      const DoctorsLoadingState(),
    );
    emit(const DoctorsLoadingState());
    final result = await doctorService.getAllSpeciality(event.jwtToken);
    print(result.toString());
    //has no error and data loaded
    if (result.item1 == null) {
      final speicailities = result.item2;

      emit(DoctorsSpecialitiesLoaded(specialities: speicailities));
    } else {
      //has error (error not null)
      emit(DoctorErrorState(result.item1!));
    }
  }```
.



class DoctorService {
  final List<DoctorSpeciality> specialities = [];
  final options = Options(
    responseType: ResponseType.json,
    receiveTimeout: 2000,
    sendTimeout: 2000,
  );
  final _dio = Dio();
  Future<Tuple<String?, List<DoctorSpeciality>>> getAllSpeciality(
      String jwtToken) async {
    specialities.clear();
    var tuple = Tuple<String?, List<DoctorSpeciality>>(null, []);

    try {
      final response = await _dio.get<List>(ApiVars.specialitiesEndPoint,
          options:
              options.copyWith(headers: {"Authorization": "Bearer $jwtToken"}));
      if (response.statusCode == 200) {
        //has no data
        if (response.data == null) {
          //set error 1
          tuple.setNewValues('No data loaded', []);
          //print it
          log(tuple.item1 ?? '');
          //return tuple with error and empty list
          return tuple;
        }
        //has data then map it into list of specialities
        response.data?.forEach((element) {
          //convert json to speciality and add it to specialities list
          specialities.add(DoctorSpeciality.fromJson(element));
        });
        //set error to null and list to specialites list
        tuple.setNewValues(null, specialities);
        return tuple;
      } else {
        //set error to error with the code and list to empty list
        tuple.setNewValues('error occur with code ${response.statusCode}', []);
        log(tuple.item1 ?? '');

        return tuple;
      }
    } on DioError catch (error) {
      //set error to error message and list to empty list
      tuple.setNewValues("doc service ${error.message}", []);
      log(tuple.item1 ?? '');
      return tuple;
    }
  }
}



I tried add droppable, sequential and didn't work

on<LoadAllSpecialities>(_loadAllSpecialities, transformer: droppable());


Solution

  • I solved the problem by adding Future before function that called inside the handler function and await it to end as code in below ///constructor called super and pass initial state...

    on<DoctorsEvent>(
         (event, emit) async {
           try {
             if (event is LoadAllSpecialities) {
               // * load all specialities of doctors from api ...
               //add await here
           await _loadAllSpecialities(event, emit);
             }
            } catch (error) {
             emit(DoctorErrorState(error.toString()));
           }
         },
       );
     }
    
               //add future here
     Future<void> _loadAllSpecialities(
         LoadAllSpecialities event, Emitter<DoctorsState> emit) async {
       emit(
         const DoctorsLoadingState(),
       );
       emit(const DoctorsLoadingState());
       final result = await doctorService.getAllSpeciality(event.jwtToken);
       //has no error and data loaded
       if (result.item1 == null) {
         final speicailities = result.item2;
    
         emit(DoctorsSpecialitiesLoaded(specialities: speicailities));
       } else {
         //has error (error not null)
         emit(DoctorErrorState(result.item1!));
       }
     }