downloadWithProgress static method
Stream<int>
downloadWithProgress({
- required String url,
- required String targetPath,
- String? token,
- int maxRetries = 10,
- CancelToken? cancelToken,
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
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,
}) {
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();
});
}
_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; // ← ADD: Store task ID when created
},
).whenComplete(() {
// Clean up cancellation listener when download completes
cancellationListener?.cancel();
});
return progress.stream;
}