downloadWithProgress static method

Stream<int> downloadWithProgress({
  1. required String url,
  2. required String targetPath,
  3. String? token,
  4. int maxRetries = 10,
  5. CancelToken? cancelToken,
  6. bool? foreground,
})

Downloads a file with smart retry logic and HTTP-aware error handling

url - File URL (any server) targetPath - Local file path to save to token - Optional authorization token (e.g., HuggingFace, custom auth) maxRetries - Maximum number of retry attempts for transient errors (default: 10) cancelToken - Optional token for cancellation foreground - Android foreground service mode:

  • null (default): auto-detect based on file size (>500MB = foreground)
  • true: always use foreground (shows notification)
  • false: never use foreground

Note: Auth errors (401/403/404) fail after 1 attempt, regardless of maxRetries. Only network errors and server errors (5xx) will be retried up to maxRetries times. Returns a stream of progress percentages (0-100)

The stream will emit DownloadCancelledException if cancelled via cancelToken.

Implementation

static Stream<int> downloadWithProgress({
  required String url,
  required String targetPath,
  String? token,
  int maxRetries = 10,
  CancelToken? cancelToken,
  bool? foreground,
}) {
  final progress = StreamController<int>();
  StreamSubscription? currentListener;
  StreamSubscription? cancellationListener;
  String? currentTaskId; // ← ADD: Store task ID for cancellation

  // Listen for cancellation
  if (cancelToken != null) {
    cancellationListener =
        cancelToken.whenCancelled.asStream().listen((_) async {
      debugPrint('🚫 Cancellation requested');

      // Cancel the actual download task
      if (currentTaskId != null) {
        debugPrint('🚫 Cancelling task: $currentTaskId');
        try {
          await FileDownloader().cancelTaskWithId(
              currentTaskId!); // ← ADD: Actually cancel the task
        } catch (e) {
          debugPrint('⚠️ Failed to cancel task: $e');
        }
      }

      if (!progress.isClosed) {
        progress.addError(
          DownloadCancelledException(
            cancelToken.cancelReason ?? 'Download cancelled',
            StackTrace.current,
          ),
        );
        progress.close();
      }
      currentListener?.cancel();
      cancellationListener?.cancel();
    });
  }

  // Configure FileDownloader and start download
  _ensureConfigured(foreground).then((_) async {
    await _downloadWithSmartRetry(
      url: url,
      targetPath: targetPath,
      token: token,
      maxRetries: maxRetries,
      progress: progress,
      currentAttempt: 1,
      currentListener: currentListener,
      cancelToken: cancelToken,
      onListenerCreated: (listener) {
        currentListener = listener;
      },
      onTaskCreated: (taskId) {
        currentTaskId = taskId;
      },
    );
  }).whenComplete(() {
    // Clean up cancellation listener when download completes
    cancellationListener?.cancel();
  });

  return progress.stream;
}