flutterflutter-http

Flutter how to abort prevent request with http package


I have a text field to search for cities. When I type a character, a request is sent to the server (API). When I enter any character. I need to cancel the previous request that is pending. Please help dear professionals, Thank you). I can't cancel my previous request

this is httpService

Future<http.Response> httpGetPublic(path) async {
  try {
    var response = await http.get(Uri.parse(path), headers: {
      HttpHeaders.contentTypeHeader: "application/json",
      HttpHeaders.hostHeader: API.hostname,
      HttpHeaders.cacheControlHeader: "no-cache",
      HttpHeaders.acceptHeader: "application/json",
      HttpHeaders.acceptEncodingHeader: "gzip, deflate, br",
    }).timeout(
      const Duration(seconds: 30),
      onTimeout: () {
        return http.Response('Connection Timeout', 408);
      },
    );

    return response;

  } catch (error) {    
    return http.Response('{"error":"${error.toString()}"}', 500);
  }
}

this method for fetching data

void fetchData(String query) async {
  cities.clear();
  _isLoading.value = true;
  final response = await httpGetPublic("${API.cities}?name=$query");
  try {
    Iterable data = jsonDecode(utf8.decode(response.bodyBytes));

    if (response.statusCode == 200) {
      List<FlightCitiesModel> res = List<FlightCitiesModel>.from(
          data.map((model) => FlightCitiesModel.fromJson(model)));

      cities.addAll(res);
    } else {
      if (response.statusCode < 430) {
         snackBarResponseError(responseData);
      }
    }
  } catch (error) {
    debugPrint("ERROR ====>");
    debugPrint(error.toString());
  }

  _isLoading.value = false;
}

this is my search TextField

TextField(
  autofocus: true,
  decoration: InputDecoration(
    isDense: true,
    border: OutlineInputBorder(
      borderRadius: BorderRadius.circular(8),
    ),
    suffixIcon: const Icon(
      Icons.search,
      size: 26,
    ),
  ),
  onChanged: (String value) {
    if (value.length > 1) {
      citiesController.setLoading();
    }
    if (_timer?.isActive ?? false) _timer!.cancel();
    _timer = Timer(const Duration(milliseconds: 150), () {
      citiesController.fetchData(value);
    });
  },
);

Solution

  • You can achieve this using http package's CancelToken feature along with dio package:

    1. Ensure you have the necessary imports for http and dart:async.
    import 'dart:async';
    import 'package:http/http.dart' as http;
    
    1. You'll need to maintain a reference to the current request and cancel it when starting a new one.
    // Create a variable to hold the current request
    http.CancelToken? _cancelToken;
    
    // Modify your existing function to accept a cancel token
    Future<http.Response> httpGetPublic(String path) async {
      // Cancel the previous request if it exists
      _cancelToken?.cancel();
    
      // Create a new cancel token for this request
      _cancelToken = http.CancelToken();
    
      try {
        var response = await http.get(
          Uri.parse(path),
          headers: {
            HttpHeaders.contentTypeHeader: "application/json",
            HttpHeaders.hostHeader: API.hostname,
            HttpHeaders.cacheControlHeader: "no-cache",
            HttpHeaders.acceptHeader: "application/json",
            HttpHeaders.acceptEncodingHeader: "gzip, deflate, br",
          },
          // Pass the cancel token to the request
          cancelToken: _cancelToken,
        ).timeout(
          const Duration(seconds: 30),
          onTimeout: () {
            // Handle timeout
            return http.Response('Connection Timeout', 408);
          },
        );
        return response;
      } catch (error) {
        return http.Response('{"error":"${error.toString()}"}', 500);
      }
    }
    

    Now when ever you call this method, it will first cancel the existing request and then calls a new one.