# Handling Timeout and Connectivity State Changes in Flutter with Dio

One of the most popular Flutter packages for dealing with http data is dio (opens new window). In real world usage we have to deal with less than perfect internet connections. Fortunately dio supports (Interceptors (opens new window)) which allows us (amount other things) to retry failed requests.

One package I came across recently for retrying requests is dio_retry (opens new window). This package is pretty simple--just add it in to the list of dio interceptors, set the maximum number of retry requests and let it go.

Matt Rešetár wrote a great article on handling connectivity state changes with yet another interceptor: Flutter Tutorial - Dio Connectivity Retry Interceptor (opens new window)

One thing not discussed was how to combine network timeout issues with connectivity state changes. For example, if we don't have an internet connection it doesn't make sense to retry the dio request. Likewise, if we have a poor internet connection and are experiencing timeouts on our requests it may make sense to retry the request (up to a specified limit). The code below shows how to handle both pretty simply.

Our shouldRetry function is below which checks to see if the error type is valid as well as to display a message to the user.

  static FutureOr<bool> shouldRetry(DioError error) async {
    // Don't retry if cancelled or if response error (e.g. 404, etc.)
    var hasRetry = error.type != DioErrorType.CANCEL &&
        error.type != DioErrorType.RESPONSE;
    // show a message to the user via flushbar
    if (hasRetry) {
      // clientConnectionType is set by a listener on the connectivity object
      var msg = clientConnectiontype == 'None' ? 'Please check your internet connection' : 
        error.type == DioErrorType.DEFAULT ? 'An error has occurred. Retrying...' :
            'A network timeout has occurred. Retrying...';
      // display toast to the user via flushbar https://pub.dev/packages/flushbar
      unawaited(Flushbar(
              message: msg,
              duration: Duration(seconds: 1),
              title: 'Connection Error')
          .show(context));
    }
    return hasRetry;
  }

Our onError handler checks the connection type first. If there is no internet connection we schedule a retry request which will run once the connection state changes. Otherwise we will retry the previous request just so long as we haven't exceeded a maximum number of requests.

The relevant code for our onError handler is below:

    try {
      if (clientconnectiontype == 'None') {
        // for an example implementation of scheduleRequestRetry see https://resocoder.com/2020/03/23/dio-connectivity-retry-interceptor-flutter-tutorial/
        return scheduleRequestRetry(err.request);
      }
      else {
        // retry with the updated options
        return await dio.request(
        err.request.path,
        cancelToken: err.request.cancelToken,
        data: err.request.data,
        onReceiveProgress: err.request.onReceiveProgress,
        onSendProgress: err.request.onSendProgress,
        queryParameters: err.request.queryParameters,
        options: err.request,
        );
      }
    } catch (e) {
      return e;
    }

With these simple changes you can handle both retry logic and connectivity state changes. For testing purposes you can set dio timeout values to artificially small values and also use a fictitious endpoint on your requests that is guaranteed to timeout.

dio.options.connectTimeout = 5000; // 5 seconds
dio.options.receiveTimeout = 5000; // 5 seconds

For testing connectivity state changes I suggest using the Android emulator since you can easily toggle airplane mode on and off. Lastly, to simulate poor networks use the Network Link Conditioner (opens new window).