Resend request if request fail on click of button in flutter dio

182 Views Asked by At

I am new to flutter I have project where I have used bloc for state management and Dio for http request.

I am showing a Snack bar on error from rest Api now I want to resend a previous request if user click on retry button, I have provided in Snack bar, how can I do that, I know that there is something like cancel Token in Dio, but I don't know how to use it and if it is possible to do so by using it.

1

There are 1 best solutions below

2
On

so, basically you want to cancel previous request and if user taps again on retry button in snackbar, so send the same new one? I think this code snippet may be useful for you if I understood your issue right. So firts I created bloc, but it os minimal example and you need to adapt it for you task

  abstract class DataEvent {
  const DataEvent();
}

class FetchDataEvent extends DataEvent {}

class RetryEvent extends DataEvent {}

class DataState extends Equatable {
  const DataState({
    required this.data,
    required this.error,
  });

  final List<Users>? data;
  final String? error;

  @override
  List<Object?> get props => [
        data,
        error,
      ];

  DataState copyWith({
    String? error,
    List<Users>? data,
  }) {
    return DataState(
      error: error ?? this.error,
      data: data ?? this.data,
    );
  }

}

class DataInitial extends DataState {
  DataInitial()
      : super(
          error: '',
          data: [],
        );
}

class DataBloc extends Bloc<DataEvent, DataState> {
  DataBloc() : super(DataInitial()) {
    on<FetchDataEvent>(_getData);
    on<RetryEvent>(_retryData);
  }
  final Dio dio = Dio();
  CancelToken cancelToken = CancelToken();

  Future<void> _getData(event, emit) async {
    try {
      print('users');
      Response response = await dio.get('https://jsonplaceholder.typicode.com/users', cancelToken: cancelToken);
      if (response.statusCode == 200) {
       final users = (response.data as List)
            .map((users) => Users.fromJson(users))
            .toList();
        emit(state.copyWith(data: users));
      }
    } catch (e) {
      print(e.toString());
      emit(state.copyWith(error: e.toString()));
    }
  }

  Future<void> _retryData(event, emit) async {
    if (!cancelToken.isCancelled) {
      cancelToken.cancel('cancel token here');
      print('cancel token here' );
      print(cancelToken.isCancelled );
    }
    cancelToken = CancelToken();
    await _getData(event, emit);
  }
}

I created get request for getting data from api first. Also I create separate _retry function for tapping on button and cancel request and after it cancelling, it will create the new token for the next request and get data from Api again. It is the minimal example and of cource it needs to be re-work for your issue. Also interceptors in dio is required for convenient eay of error catching, it seems to me it has nothing to do with cancel request. But I can be wrong. Also there is some UI for checking how it works:

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  void initState() {
    BlocProvider.of<DataBloc>(context).add(FetchDataEvent());
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: BlocBuilder<DataBloc, DataState>(
        builder: (context, state) {
          if (state.error!.isNotEmpty) {
            return Center(child: Text(state.error ?? 'err'));
          }
            return ListView.builder(
              itemCount: state.data?.length ?? 0,
              itemBuilder: (context, index) {
                return ListTile(title: Text(state.data?[index].name ?? ''));
              },
            );
          }
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          BlocProvider.of<DataBloc>(context).add(RetryEvent());
        },
        child: Icon(Icons.refresh),
      ),
    );
  }
}