httpRetry<T extends Response> function Null safety

Future<Map<String, dynamic>?> httpRetry<T extends Response>(
  1. FutureOr<T> fn(
      ),
    1. {Duration delayFactor = const Duration(milliseconds: 200),
    2. double randomizationFactor = 0.25,
    3. Duration maxDelay = const Duration(seconds: 30),
    4. int maxAttempts = 8,
    5. FutureOr<bool> retryIf(
      1. Exception
      )?,
    6. FutureOr<void> onRetry(
      1. Exception
      )?}
    )

    Implementation

    Future<Map<String, dynamic>?> httpRetry<T extends http.Response>(
      FutureOr<T> Function() fn, {
      Duration delayFactor = const Duration(milliseconds: 200),
      double randomizationFactor = 0.25,
      Duration maxDelay = const Duration(seconds: 30),
      int maxAttempts = 8,
      FutureOr<bool> Function(Exception)? retryIf,
      FutureOr<void> Function(Exception)? onRetry,
    }) async {
      var attempt = 1;
      while (true) {
        final options = RetryOptions(
          delayFactor: delayFactor,
          randomizationFactor: randomizationFactor,
          maxDelay: maxDelay,
          maxAttempts: maxAttempts,
        );
    
        var result = await options.retry(fn,
            retryIf: retryIf ?? (e) => e is IOException || e is TimeoutException,
            onRetry: onRetry);
    
        if (result.statusCode == 503 ||
            result.statusCode == 502 ||
            result.statusCode == 504) {
          if (attempt >= maxAttempts) {
            throw HttpException(
                "The server could not be reached. Please try again later.");
          }
          await Future<void>.delayed(options.delay(attempt));
          attempt++;
          continue;
        }
    
        final body = result.body.isEmpty
            ? "{}"
            : result.body.startsWith("{")
                ? result.body
                : result.body.startsWith("<html")
                    ? "{}"
                    : "\{\"error\": \"${result.body.replaceAll("\"", "'")}\"\}";
    
        final jsonResponse = jsonDecode(body) as Map<String, dynamic>?;
    
        if (result.statusCode < 200 || result.statusCode >= 300) {
          if (jsonResponse!["error"] != null) {
            var error = jsonResponse["error"].toString();
            if (jsonResponse["error_description"] != null)
              error += ": ${jsonResponse["error_description"]}";
            throw HttpResponseException(
                ERROR_MESSAGE_FORMAT.replaceAll("%2", error));
          } else {
            throw HttpResponseException(
                ERROR_MESSAGE_FORMAT.replaceAll("%2", "unknown_error"));
          }
        }
    
        return result.body.isEmpty ? null : jsonResponse;
      }
    }