downloadFile method

Future<String?> downloadFile({
  1. required String downloadFileUrl,
  2. void onProgress(
    1. double progress
    )?,
  3. @Deprecated('Use a try-catch block to handle FileDownloadException instead.') void onError(
    1. String error
    )?,
  4. void onTimeLeft(
    1. String error
    )?,
  5. void onSpeed(
    1. String error
    )?,
  6. void onTotalSize(
    1. String error
    )?,
  7. void onDownloadedSize(
    1. String error
    )?,
  8. String? downloadFileName,
})

Implementation

Future<String?> downloadFile({
  required String downloadFileUrl,
  void Function(double progress)? onProgress,
  @Deprecated(
      'Use a try-catch block to handle FileDownloadException instead.')
  void Function(String error)? onError,
  void Function(String timeLeft)? onTimeLeft,
  void Function(String speed)? onSpeed,
  void Function(String totalSize)? onTotalSize,
  void Function(String downloadedSize)? onDownloadedSize,
  String? downloadFileName,
}) async {
  _cancelToken = CancelToken();

  int lastBytes = 0;
  DateTime lastUpdateTime = DateTime.now();

  try {
    Directory? directory = await getExternalStorageDirectory();

    if (directory == null) {
      final exception = FileDownloadException(
        type: DownloadErrorType.storageAccessDenied,
        originalError: "Unable to access storage directory",
      );
      return _handleErrorBridge(
          exception, "Unable to access storage directory", onError);
    }

    String savePath =
        "${directory.path}/${downloadFileName ?? "downloadApk"}.apk";

    Response response = await _dio.download(
      downloadFileUrl,
      savePath,
      cancelToken: _cancelToken,
      onReceiveProgress: (count, total) {
        if (total > 0) {
          if (onProgress != null) {
            onProgress(count / total);
          }

          if (onTotalSize != null) {
            onTotalSize(_formatBytes(total));
          }

          if (onDownloadedSize != null) {
            onDownloadedSize(_formatBytes(count));
          }

          DateTime now = DateTime.now();
          Duration interval = now.difference(lastUpdateTime);

          // Update speed and time left every 500ms or when finished
          if (interval.inMilliseconds >= 500 || count == total) {
            int bytesSinceLast = count - lastBytes;
            double secondsSinceLast = interval.inMilliseconds / 1000.0;

            if (secondsSinceLast > 0) {
              double speedBps = bytesSinceLast / secondsSinceLast;

              if (onSpeed != null) {
                onSpeed(_formatSpeed(speedBps));
              }

              if (onTimeLeft != null && speedBps > 0) {
                double remainingBytes = (total - count).toDouble();
                double remainingSeconds = remainingBytes / speedBps;
                onTimeLeft(_formatTimeLeft(remainingSeconds));
              }
            }

            lastBytes = count;
            lastUpdateTime = now;
          }
        }
      },
    );

    if (response.statusCode == 200) {
      return savePath;
    } else {
      final exception = FileDownloadException(
        type: DownloadErrorType.badResponse,
        statusCode: response.statusCode,
        originalError: "Server returned status: ${response.statusCode}",
      );
      return _handleErrorBridge(
          exception, "Unable to download the file", onError);
    }
  } on DioException catch (e, sc) {
    log("DioException in downloadFile", error: e, stackTrace: sc);
    final exception = FileDownloadException(
      type: _mapDioErrorToEnum(e.type),
      statusCode: e.response?.statusCode,
      originalError: e,
    );
    String legacyErrorString = _getLegacyErrorMessage(e);
    return _handleErrorBridge(exception, legacyErrorString, onError);
  } catch (e, sc) {
    log("Unknown Exception in downloadFile", error: e, stackTrace: sc);
    final exception = FileDownloadException(
        type: DownloadErrorType.unknown, originalError: e);
    return _handleErrorBridge(
        exception, "Error Occurred while downloading the file", onError);
  }
}