PackageTrack
Sign in Get early access

background_downloader

A multi-platform background file downloader and uploader. Define the task, enqueue and monitor progress

9.5.8 184K downloads/mo #885 most downloaded on pub.dev 781flyingdutchman/background_downloader

What this package is like to depend on

Last release 11 days ago

13 Aug 2026

Release timing varies

gaps range from 8 days to 3 months

Nearly every release is documented

notes for 132 of 133 stable releases

Nothing withdrawn

no release was ever pulled

4 years old

133 releases · first in 2022

18 releases in the last 12 months

see the full history below

Release timeline

133 releases · Sep 2022 to Aug 2026
2023 2024 2025 2026
Release Pre-release

Releases

latest 60 of 133
  1. 9.5.8 13 Aug 2026
    Release notes

    Merge pull request #697 from 781flyingdutchman/issue-695-require-wifi…

    Open source →
    Release notes
    • Add alsoRestartUploads parameter to FileDownloader.requireWiFi to optionally cancel and restart running upload tasks when updating WiFi requirement (fixes #695)
    • [iOS] Fixes typo in PrivacyInfo.xcprivacy for photos and videos data collection key (NSPrivacyCollectedDataTypePhotosorVideos), resolving App Store submission validation errors (fixes #694)
    • [Documentation] Adds comprehensive documentation for TaskStatusUpdate and TaskProgressUpdate in doc/status_updates.md (fixes #684)
    Open source →
  2. 9.5.7 27 Jul 2026
    Release notes
    • [Android] Bug fixes:
      • Fixes group notification sticking in running state when cancelling tasks from holding queue
      • Fixes ConcurrentModificationException in canSendCancellation when processing concurrent status updates
    • [iOS] Bug fixes:
      • Fixes group notification sticking in running state when cancelling tasks from holding queue
      • Serializes access to group notification registry to prevent EXC_BAD_ACCESS race condition under concurrent status updates
    • [General] Defer JsonProcessor idle shutdown while requests are pending to prevent unexpected background isolate shutdown errors
    Open source →
    Release notes
    • [Android] Bug fixes:
      • Fixes group notification sticking in running state when cancelling tasks from holding queue
      • Fixes ConcurrentModificationException in canSendCancellation when processing concurrent status updates
    • [iOS] Bug fixes:
      • Fixes group notification sticking in running state when cancelling tasks from holding queue
      • Serializes access to group notification registry to prevent EXC_BAD_ACCESS race condition under concurrent status updates
    • [General] Defer JsonProcessor idle shutdown while requests are pending to prevent unexpected background isolate shutdown errors
    Open source →
  3. 9.5.6 12 Jul 2026
    Release notes
    • [Android] Bug fixes and build improvements:
      • Fixes Application Not Responding (ANR) crash when canceling binary uploads via notification action (fixes #664)
      • Corrects ETag validation logic and saves the correct server ETag during a timeout-pause (fixes #683)
      • Adds fallback to legacy kotlin-android plugin in Gradle build if builtInKotlin is configured to false (closes #674)
    • [iOS] Swift Package Manager (SPM) and build fixes:
      • Resolves package dependency resolution errors when using SPM by removing invalid FlutterFramework dependency path in Package.swift
      • Restores compatibility with older Flutter SDKs in the example app's AppDelegate.swift
    • [Chore / Maintenance] Updated GitHub Actions workflow configurations to resolve Node 20 deprecation warnings
    Open source →
    Release notes
    • [Android] Bug fixes and build improvements:
      • Fixes Application Not Responding (ANR) crash when canceling binary uploads via notification action (fixes #664)
      • Corrects ETag validation logic and saves the correct server ETag during a timeout-pause (fixes #683)
      • Adds fallback to legacy kotlin-android plugin in Gradle build if builtInKotlin is configured to false (closes #674)
    • [iOS] Swift Package Manager (SPM) and build fixes:
      • Resolves package dependency resolution errors when using SPM by removing invalid FlutterFramework dependency path in Package.swift
      • Restores compatibility with older Flutter SDKs in the example app's AppDelegate.swift
    • [Chore / Maintenance] Updated GitHub Actions workflow configurations to resolve Node 20 deprecation warnings
    Open source →
  4. 9.5.5 27 May 2026
    Release notes
    • [Android] Bug fixes and performance improvements:
      • Fixes a potential UI freeze during application startup
      • Prevents App Not Responding (ANR) crashes when tapping notification actions (Cancel/Pause/Resume) under heavy system load
      • Fixes group notifications running in the foreground (issue #648)
      • Migrated build configuration to Flutter's built-in Kotlin Gradle plugin configuration and upgraded Gradle to 8.14
    • [iOS] Group notification and permission improvements:
      • Fixes redundant updates and visual "flashing" of group notifications (fixes #656)
      • Migrated the example app to native Swift Package Manager (SPM) and added environment-variable-based compile-time permission bypass flags for SPM users
      • Removed 'Background Fetch' requirement from documentation and example plist files, preventing unexpected wake-ups when the device is locked and resolving potential keychain access issues (fixes #668)
    • [Desktop] Temp directory fallback:
      • Added fallback to the target download directory on desktop platforms when the system temporary directory is inaccessible (e.g., in macOS app sandbox mode, fixes #649)
    • [Chore / Maintenance] Code formatting updated to match the latest Dart formatter
    Open source →
    Release notes
    • [Android] Bug fixes and performance improvements:
      • Fixes a potential UI freeze during application startup
      • Prevents App Not Responding (ANR) crashes when tapping notification actions (Cancel/Pause/Resume) under heavy system load
      • Fixes group notifications running in the foreground (issue #648)
      • Migrated build configuration to Flutter's built-in Kotlin Gradle plugin configuration and upgraded Gradle to 8.14
    • [iOS] Group notification and permission improvements:
      • Fixes redundant updates and visual "flashing" of group notifications (fixes #656)
      • Migrated the example app to native Swift Package Manager (SPM) and added environment-variable-based compile-time permission bypass flags for SPM users
      • Removed 'Background Fetch' requirement from documentation and example plist files, preventing unexpected wake-ups when the device is locked and resolving potential keychain access issues (fixes #668)
    • [Desktop] Temp directory fallback:
      • Added fallback to the target download directory on desktop platforms when the system temporary directory is inaccessible (e.g., in macOS app sandbox mode, fixes #649)
    • [Chore / Maintenance] Code formatting updated to match the latest Dart formatter
    Open source →
  5. 9.5.4 27 Feb 2026
    Release notes
    • Add TaskOptions to DataTask with similar functionality as in DownloadTask
    • Bug fixes
    Open source →
    Release notes
    • Add TaskOptions to DataTask with similar functionality as in DownloadTask
    • Bug fixes
    Open source →
  6. 9.5.3 23 Feb 2026
    Release notes

    V9.5.3 Fix race condition in TaskWorker where setForeground could…

    Open source →
    Release notes
    • [Android] Fix race condition in TaskWorker where setForeground could crash for finished tasks
    • Add documentation
    • Add integration testing documentation and steps
    Open source →
  7. 9.5.2 14 Jan 2026
    Release notes

    V9.5.2 Moves LocalStorePersistentStorage to isolate model to off-load…

    Open source →
    Release notes
    • Moves LocalStorePersistentStorage to isolate model to off-load i/o operations, and unwinds Future.wait to loop
    Open source →
  8. 9.5.1 09 Jan 2026
    Release notes
    • [iOS] Support for UISceneDelegate in iOS 13.0 and above
    Open source →
  9. 9.5.0 31 Dec 2025
    Release notes
    • Introduces Android User Initiated Data Transfer (UIDT), triggered when task priority is set to 0 (max priority) on Android 14+, provided a notification is also configured. Explanation of Android execution options:
      • WorkManager (default and preferred for most tasks): Used for most tasks. Subject to a 9-minute execution limit in the background. If the task is not finished within 9 minutes, it will be terminated (unless allowPause is true, in which case it will pause and resume).
      • Foreground Service: Triggered via Config.runInForeground. Displays a persistent notification and is not subject to the 9-minute limit on many Android versions. Requires FOREGROUND_SERVICE_DATA_SYNC permission on Android 14+.
      • User Initiated Data Transfer (UIDT): Triggered by setting priority: 0 on Android 14+. This is a specialized JobScheduler mode that does not have the 9-minute limit and is designed for large, user-aware data transfers. It requires a notification to be shown.
        • Note: Using UIDT requires adding the RUN_USER_INITIATED_JOBS permission to your AndroidManifest.xml, as well as a service declaration for .UIDTJobService. See the README for details.
    • [Android] Fixes HoldingQueue logic to correctly include UIDT jobs and prevent race conditions
    • [Android] Properly captures WorkManager cancellations to ensure task status is updated to failed
    • [Android] Improvements for Android 14+ compatibility and Android 15 timeout logging
    • [Android] Updated minimum Kotlin compiler version to 2.1.0
    • [iOS] Updates to openFile and privacy manifest for iOS 16/17 compatibility
    • Replaces sync with async IO in LocalStorePersistentStorage to prevent UI stutter
    • [General] Documentation refactor: broken down README into separate files in doc/ directory
    Open source →
    Release notes
    • Introduces Android User Initiated Data Transfer (UIDT), triggered when task priority is set to 0 (max priority) on Android 14+, provided a notification is also configured. Explanation of Android execution options:
      • WorkManager (default and preferred for most tasks): Used for most tasks. Subject to a 9-minute execution limit in the background. If the task is not finished within 9 minutes, it will be terminated (unless allowPause is true, in which case it will pause and resume).
      • Foreground Service: Triggered via Config.runInForeground. Displays a persistent notification and is not subject to the 9-minute limit on many Android versions. Requires FOREGROUND_SERVICE_DATA_SYNC permission on Android 14+.
      • User Initiated Data Transfer (UIDT): Triggered by setting priority: 0 on Android 14+. This is a specialized JobScheduler mode that does not have the 9-minute limit and is designed for large, user-aware data transfers. It requires a notification to be shown.
        • Note: Using UIDT requires adding the RUN_USER_INITIATED_JOBS permission to your AndroidManifest.xml, as well as a service declaration for .UIDTJobService. See the README for details.
    • [Android] Fixes HoldingQueue logic to correctly include UIDT jobs and prevent race conditions
    • [Android] Properly captures WorkManager cancellations to ensure task status is updated to failed
    • [Android] Improvements for Android 14+ compatibility and Android 15 timeout logging
    • [Android] Updated minimum Kotlin compiler version to 2.1.0
    • [iOS] Updates to openFile and privacy manifest for iOS 16/17 compatibility
    • Replaces sync with async IO in LocalStorePersistentStorage to prevent UI stutter
    • [General] Documentation refactor: broken down README into separate files in doc/ directory
    Open source →
  10. 9.4.4 28 Dec 2025
    Release notes
    • [Android] MinSDK is now 23 (was 21), required because the latest Android WorkManager is required to properly support Android 15+
    • [Android] Fixes bug fix related to foreground service in Android 15+
    • Performance improvements related to Task.filePath, Task.split and ParallelDownloadTask pause
    Open source →
    Release notes
    • [Android] MinSDK is now 23 (was 21), required because the latest Android WorkManager is required to properly support Android 15+
    • [Android] Fixes bug fix related to foreground service in Android 15+
    • Performance improvements related to Task.filePath, Task.split and ParallelDownloadTask pause
    Open source →
  11. 9.4.3 21 Dec 2025
    Release notes
    • Improve JSON processing to reduce UI jank
    • Optimize database operations
    • Add database.cleanUp() to simplify and automate database maintenance, and added database clean-up to start() command
    • Fix iOS background callback performance
    Open source →
    Release notes
    • Improve JSON processing to reduce UI jank
    • Optimize database operations
    • Add database.cleanUp() to simplify and automate database maintenance, and added database clean-up to start() command
    • Fix iOS background callback performance
    Open source →
  12. 9.4.2 18 Dec 2025
    Release notes
    • Improve Android notification handling
    • Add local test server to reduce test suite flakiness
    • Improve code documentation
    Open source →
  13. 9.4.1 10 Dec 2025
    Release notes
    • Refine TaskQueue pauseAll/resumeAll to support specific tasks and groups. Updated TaskQueue interface to accept optional tasks and group parameters in pauseAll and resumeAll and implemented this for the MemoryTaskQueue
    • Change platform checks from Platform.isAndroid etc to defaultTargetPlatform which should lead to better tree shaking and smaller memory size
    Open source →
  14. 9.4.0 21 Nov 2025
    Release notes
    • Improves concurrency on mobile platforms by moving JSON encoding and decoding to an isolate, and introducing a job queue to ensure ordered message execution.
    • Adds Config.skipExistingFiles configuration option to skip the download if the destination file already exists (and conditionally does this only for files greater than a certain size)
    Open source →
  15. 9.3.0 02 Nov 2025
    Release notes

    Main change is in Android concurrency, triggered by the Great Thread Merge that causes UI blocking issues for recent versions of Flutter. The concurrency approach has been changed, but note this can cause minor changes in - for example - the sequence in which certain events happen.

    • Feature improvements

      • Add pause and resume functionality to TaskQueue using pauseAll and resumeAll. These methods are now called when pausing or resuming all tasks using the FileDownloader
    • Bug fixes

      • Fix issue with MultiUploadTask when uploading only one file
      • UriUtils subclasses are now private, as they should have been to start with
      • Address Dart analyzer recommendations
    Open source →
  16. 9.2.6 20 Sep 2025
    Release notes
    • Bug fix

      • [Android] Fix Tap to open (on notification) bug, introduced in 9.2.4
    • Version upgrades

      • [Android] compileSDK set to 36, targetSDK for example app set to 36, Kotlin compiler in example app set to 2.1.0
    Open source →
  17. 9.2.5 13 Sep 2025
    Release notes
    • Minor bug fixes
      • [iOS] catch file write errors in multi-part uploads
      • [iOS] fix optional unwrap error
    Open source →
  18. 9.2.4 11 Sep 2025
    Release notes
    • Minor bug fixes
      • [iOS] Notification update after killed app resumes
      • [Android] Use of UTF-8 characters in post field
      • Multipart upload remove extraneous CR/LF after header
      • [Android] Fallback for expedited tasks when encountering OS error
      • [Android] fix enqueAll and similar calls when no Activity attached
    Open source →
  19. 9.2.3 19 Jul 2025
    Release notes
    • Minor bug fixes
      • [Android] Unmarking temp files as cache after move to download destination
      • [iOS] Improved permissions bypassing
      • [Android] Use of $ sign in notification substitutions
      • [iOS] Crash on returning app to foreground when app is suspended
    Open source →
  20. 9.2.2 17 May 2025
    Release notes
    • Adds option to set the Content-Disposition header for binary uploads
    • Fixes bug on iOS when using openFile with a filepath containing spaces

    For binary uploads, the Content-Disposition header will be:

    • set to 'attachment = "filename"' if the task.headers field does not contain an entry for 'Content-Disposition' (the prior default)
    • not set at all (i.e. omitted) if the task.headers field contains an entry for 'Content-Disposition' with the value '' (an empty string)
    • set to the value of task.headers['Content-Disposition'] in all other cases
    Open source →
  21. 9.2.1 27 Apr 2025
    Release notes
    • Minor bug fixes
    Open source →
  22. 9.2.0 15 Mar 2025
    Release notes
    • Adds updates broadcast stream to the database property, which emits every TaskRecord update made to the database.
    • Fixes issue with start and rescheduleKilledTasks when using group names for tasks
    • Minor bug fixes and enhancements
    Open source →
  23. 9.1.1 05 Mar 2025
    Release notes
    • Adds optional group parameter to resumeAll
    • Fixes concurrency bugs in Android
    Open source →
  24. 9.1.0 02 Mar 2025
    Release notes
    • Adds pauseAll, cancelAll and resumeAll
    • Improves handling enqueuAll to not starve the UI thread
    • Fixes compilation bug when compiling with XCode 16.2
    Open source →
  25. 9.0.0 23 Feb 2025
    Release notes
    • Introduces URI operations, including file/photo/video/directory pickers, under the uri property, and UriDownloadTask and UriUploadTask. See Working with URIs.
    • [Breaking] removes references to asAndroidUri - use the new methods on the uri property instead
    • Adds enqueueAll to enqueue a list of tasks (non-blocking and with better performance when compared to calling enqueue for each task)
    • Adds native callback beforeTaskStartCallback to TaskOptions, that is called just before the task starts executing. If it returns a non-null TaskStatusUpdate then this forces the task to finish before it starts, with that TaskStatusUpdate
    • Adds option to configure a canceled notification (instead of cancellation defaulting to the error notification)
    • Fixes bug with request when using PUT or PATCH requests
    • Fixes bug for multi-part uploads when field values are formatted as a JSON string and contain non-ASCII characters
    • Bumps minimum iOS version to 14

    Uri operations

    Uri operations abstract away platform differences, including content provider on Android (used for the Storage Access Framework), and file, photo and directory pickers on iOS and Android. This enables largely platform-independent code for file operations, and on Android downloads directly to destination, bypassing the temp file in internal storage.

    The FileDownloader().uri property provides access to a set of utility functions for working with URIs, including:

    • pickDirectory(): Opens a directory picker dialog and returns the selected directory's URI.
    • pickFile(): Opens a file picker dialog and returns the selected file's URI.
    • pickFiles(): Opens a file picker dialog and allows selection of multiple files, returning their URIs in a list.
    • createDirectory(): Creates a new directory within a specified parent directory URI.
    • getFileBytes(): Retrieves the file data (bytes) for a given URI.
    • copyFile(): copies a file from a source uri to a destination. Destination can be a Uri, a File or a String containing a file path
    • moveFile(): moves a file from a source uri to a destination. Destination can be a Uri, a File or a String containing a file path. If the move fails, it is possible that the file was copied but the source was not deleted
    • deleteFile(): Deletes the file at the given URI.
    • openFile(): Opens the file at a given URI.
    • moveToSharedStorage(): Moves a file to a shared storage location.
    • activate(): Activates a previously accessed directory or file. Only relevant if you use persistedUriPermission or use the photo/video picker.

    The pick... methods and createDirectory take an optional persistedUriPermission argument (defaults to false) that when true registers the picked directory with the OS, allowing access in a later session.

    New/modified Task types:

    • UriDownloadTask: Downloads a file to a specified directory URI. On Android, this bypasses the temp file used in the traditional approach and downloads directly to the destination.
    • UriUploadTask: Uploads a file from a given file URI. If the filename is omitted, it will be based on the task's URL.
    • MultiUploadTask: now accepts Uri where previously only filename or file path was allowed
    Open source →
  26. 8.9.5 15 Mar 2025
    Release notes
    • Fixes issue with start and rescheduleKilledTasks when using group names for tasks
    Open source →
  27. 8.9.4 25 Jan 2025
    Release notes
    • Modifies the interval between TaskProgressUpdate such that an update is sent at least once every 2.5 seconds if progress has been made, even if it less than 2% of the file size
    • Improves rescheduleKilledTasks to also reschedule tasks marked as waitingToRetry but not registered as such
    Open source →
  28. 8.9.3 18 Jan 2025
    Release notes
    • Adds start which ensures the various start-up calls are executed in the correct order. Use this instead of calling trackTasks, resumeFromBackground and rescheduleKilledTasks separately
    • Adds rescheduleKilledTasks which will compare enqueued/running tasks in the database with those active in the downloader, and reschedules those that have been killed by the user
    • [iOS] Removes limit on range of partial file uploads using the Range header (was 2GB)
    Open source →
  29. 8.9.2 16 Jan 2025
    Release notes
    • Adds rescheduleMissingTasks, which compares tasks in the database with tasks in the native downloader, and reschedules those tasks that are present in the database (and therefore should be enqueued or running) but are not present in the native downloader. Returns two lists of Task - successfully rescheduled ones, and tasks that failed to reschedule.
    • Upgraded minimum Dart SDK to 3.5.0 / Flutter SDK 3.24.0 to stay in sync with dependency updates
    • [Android] Fix bug when uploading files greater than 2GB, that was introduced in V8.9.0
    Open source →
  30. 8.9.1 12 Jan 2025
    Release notes
    • [iOS] Adds Privacy Manifest
    • [iOS] Adds support for Swift Package Manager and defaults the example app to using it
    Open source →
  31. 8.9.0 10 Jan 2025
    Release notes
    • Adds options field to Task, which take a TaskOptions object to configure less common task specific options - currently onTaskStart, onTaskFinished and auth
      • onTaskStart is a callback with signatureFuture<Task?> Function(Task original), called just before the task starts executing. Your callback receives the original task about to start, and can modify this task if necessary. If you make modifications, you return the modified task - otherwise return null to continue execution with the original task. You can only change the task's url (including query parameters) and headers properties - making changes to any other property may lead to undefined behavior.
      • onTaskFinished is a callback with signature Future<void> Function(TaskStatusUpdate taskStatusUpdate), called when the task has reached a final state (regardless of outcome). Your callback receives the final TaskStatusUpdate and can act on that.
      • auth is an optional Auth object that helps manage accessToken and accessToken refresh - see the README for details
      • NOTE: The callback functionality is experimental for now, and its behavior may change without warning in future updates. Please provide feedback on callbacks
    • Upgrades Android Java version to version 17 (modifies build.gradle)
    • Fixes concurrency issue on iOS
    • Changes how numTotal is calculated for group notifications: numTotal is now increment when a task is enqueued, instead of when it starts running. Note that this can lead to a '0/20 files' type notification if the tasks are enqueued but cannot start due to a constraint such as requiring WiFi
    • Expands type of Android URI that can be used to upload a file (was MediaStore URIs only, now accepts any Android URI, e.g. one provided by a document provider such as a file picker)
    Open source →
  32. 8.8.1 18 Dec 2024
    Release notes
    • Fixes Android bug where timeout timer is not cleaned up after use
    Open source →
  33. 8.8.0 10 Nov 2024
    Release notes
    • [iOS] Adds configuration option to exclude downloaded files from iCloud backup
    • Adds allGroups parameter to allTasks and allTaskIds methods, to retrieve all tasks regardless of group
    • [Android] Fixes issue with un-commanded restart of a download in specific scenarios
    Open source →
  34. 8.7.1 08 Nov 2024
    Release notes
    • Fix for compilation issue on Kotlin 2
    Open source →
  35. 8.7.0 03 Nov 2024
    Release notes
    • Adds option to specify a file location for upload using a Mediastore URI on Android, using UploadTask.fromUri. A Mediastore URI can also be requested from methods moveToSharedStorage and pathInSharedStorage by adding asAndroidUri = true to the call.
    • Fixes bug with ParallelDownload when an error occurs
    • Updates dependency on package mime to 2.0, therefore also Dart 3.2 (Flutter 3.16.0) or greater. Use dependency_overrides in pubspec.yaml to resolve (background_downloader works with 1.0 and 2.0)
    Open source →
  36. 8.6.0 24 Oct 2024
    Release notes
    • Adds option for partial uploads, for binary uploads only. Set the byte range by adding a "Range" header to your binary UploadTask, e.g. a value of "bytes=100-149" will upload 50 bytes starting at byte 100. You can omit the range end (but not the "-") to upload from the indicated start byte to the end of the file. The "Range" header will not be passed on to the server. Note that on iOS an invalid range will cause enqueue to fail, whereas on Android and Desktop the task will fail when attempting to start.
    • Fixes issue in iOS when multiple Flutter engines register the plugin
    • Fixes issue with lingering HTTP connections on desktop
    • Adds CI workflow (formatting, lints, build Android, build iOS)
    Open source →
  37. 8.5.6 27 Sep 2024
    Release notes
    • Fixes desktop upload cancellation bug
    • Adds Url-encoding of Content-Disposition header for binary uploads. Note for multipart uploads, filename is 'browserEncoded' which does not encode Non-ASCII characters
    • Fixes bug with creation of unique filename on iOS
    Open source →
  38. 8.5.5 05 Sep 2024
    Release notes
    • Fixes concurrent database write bug for TaskRecords
    Open source →
  39. 8.5.4 02 Sep 2024
    Release notes
    • If the value of a fields entry of an UploadTask is in JSON format (defined as start/end with {} or []) then the field's mime-type will be set to application/json, whereas it would not have been set prior
    • Fixes an issue on iOS where use of the holding queue can lead to deadlock
    • For Windows, when using BaseDirectory.root, fixes an issue with Task.split and Task.baseDirectoryPath. When using BaseDirectory.root on Windows, your task's directory must contain the drive letter.
    Open source →
  40. 8.5.3 09 Aug 2024
    Release notes
    • Bug fixes
    • Improvements to documentation
    Open source →
  41. 8.5.2 19 May 2024
    Release notes
    • Removes references to dart:html to allow web compilation using WASM. Note the package still does not work on the web
    • Adds auto-decode of post field if Map or List. Throws if jsonEncode cannot convert the object, in which case you have to encode it yourself using a custom encoder
    Open source →
  42. 8.5.1 15 May 2024
    Release notes
    • Fixes an issue where temporary files were not deleted when canceling a paused parallel download task
    Open source →
  43. 8.5.0 10 May 2024
    Release notes
    • Adds DataTask for scheduled server requests
    • Fixes bug omitting Content-Type header for iOS uploads, and Content-Disposition header for desktop uploads

    DataTask

    The downloader already supported server requests for immediate execution using FileDownloader.request(Request request). This change adds the option to scheduled a server request similar to scheduling any other Task.

    To schedule a server request using the background mechanism (e.g. if you want to wait for WiFi to be available), create and enqueue a DataTask. A DataTask is similar to a DownloadTask except it:

    • Does not accept file information, as there is no file involved
    • Does not allow progress updates
    • Accepts post data as a String, or
    • Accepts json data, which will be converted to a String and posted as content type application/json
    • Accepts contentType which will set the Content-Type header value
    • Returns the server responseBody, responseHeaders and possible taskException in the final TaskStatusUpdate fields

    Typically you would use enqueue to enqueue a DataTask and monitor the result using a listener or callback, but you can also use transmit to enqueue and wait for the final result of the DataTask.

    Open source →
  44. 8.4.3 12 Apr 2024
    Release notes
    • Fixes iOS/Android issue where retrieveLocallyStoredData retrieves only a basic TaskStatusUpdate, without responseCode, responseBody etc
    Open source →
  45. 8.4.2 08 Apr 2024
    Release notes
    • Fixes iOS/Android bug with ParallelDownloadTask hanging when number of chunks exceeds ~10
    Open source →
  46. 8.4.1 20 Mar 2024
    Release notes
    • Fixes Android bug when using Config.runInForeground that can lead to a crash
    Open source →
  47. 8.4.0 17 Mar 2024
    Release notes
    • Adds optional holding queue to manage how many tasks are executed concurrently
    • Fixes bug with using unique parameter in context of server suggested filename
    • Transition from imperative to declarative Gradle plugin application, see here

    Holding queue

    Once you enqueue a task with the FileDownloader it is added to an internal queue that is managed by the native platform you're running on (e.g. Android). Once enqueued, you have limited control over the execution order, the number of tasks running in parallel, etc, because all that is managed by the platform. If you want more control over the queue, you need to use a TaskQueue or a HoldingQueue:

    • A TaskQueue is a Dart object that you can add to the FileDownloader. You can create this object yourself (implementing the TaskQueue interface) or use the bundled MemoryTaskQueue implementation. This queue sits "in front of" the FileDownloader and instead of using the enqueue and download methods directly, you now simply add your tasks to the TaskQueue. Because this is a Dart object, the queue will suspend when the OS suspends your application, and if the app gets killed, tasks held in the TaskQueue will be lost (unless you have implemented persistence)
    • A HoldingQueue is native to the OS and can be configured using FileDownloader().configure to limit the number of concurrent tasks that are executed (in total, by host or by group). When using this queue you do not change how you interact with the FileDownloader, but you cannot implement your own holding queue. Because this queue is native, it will continue to run when your app is suspended by the OS, but if the app is killed then tasks held in the holding queue will be lost (unlike tasks already enqueued natively, which persist)

    This update adds the holding queue.

    Use a holding queue to limit the number of tasks running concurrently. Calling await FileDownloader().configure(globalConfig: (Config.holdingQueue, (3, 2, 1))) activates the holding queue and sets the constraints maxConcurrent to 3, maxConcurrentByHost to 2, and maxConcurrentByGroup to 1. Pass null for no constraint for that parameter.

    Using the holding queue adds a queue on the native side where tasks may have to wait before being enqueued with the Android WorkManager or iOS URLSessions. Because the holding queue lives on the native side (not Dart) tasks will continue to get pulled from the holding queue even when the app is suspended by the OS. This is different from the TaskQueue, which lives on the Dart side and suspends when the app is suspended by the OS

    When using a holding queue:

    • Tasks will be taken out of the queue based on their priority and time of creation, provided they pass the constraints imposed by the maxConcurrent values
    • Status messages will differ slightly. You will get the TaskStatus.enqueued update immediately upon enqueuing. Once the task gets enqueued with the Android WorkManager or iOS URLSessions you will not get another "enqueue" update, but if that enqueue fails the task will fail. Once the task starts running you will get TaskStatus.running as usual
    • The holding queue and the native queues managed by the Android WorkManager or iOS URLSessions are treated as a single queue for queries like taskForId and cancelTasksWithIds. There is no way to determine whether a task is in the holding queue or already enqueued with the Android WorkManager or iOS URLSessions
    Open source →
  48. 8.3.0 11 Mar 2024
    Release notes
    • Adds responseStatusCode to TaskStatusUpdate for tasks that result in TaskStatus.complete or TaskStatus.notFound (null otherwise).
    • Adds Task.split to extract the baseDirectory, directory and filename from an absolute filePath or a File. This is saver than using .fromFile and preferred
    • Adds UploadTask.fromFile to create an UploadTask from an existing File object. Note that this will create a task with an absolute path reference and BaseDirectory.root, which can cause problems on mobile platforms, so use with care
    • Fixes bug on Android API 34 when using configuration Config.runInForeground

    Extracting baseDirectory, directory and filename from a filePath or File

    If you already have a path to a file or a File object, you can extract the values for baseDirectory, directory and filename using Task.split to create the task:

    final (baseDirectory, directory, filename) = await Task.split(filePath: yourPath);
    final task = UploadTask(
            url: 'https://yourserver.com',
            baseDirectory: baseDirectory,
            directory: directory,
            filename: filename);
    

    Using foreground service on Android targeting API 34

    If targeting API 34 or greater, you must add to your AndroidManifest.xml a permission declaration <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> and the foreground service type definition (under the application element):

    <service
      android:name="androidx.work.impl.foreground.SystemForegroundService"
      android:foregroundServiceType="dataSync"
      tools:node="merge" />
    
    Open source →
  49. 8.2.1 21 Feb 2024
    Release notes
    • Adds option to specify multiple values for a single field name in the UploadTask.fields property by formatting the value as '"value1", "value2", "value3"' (note the double quotes and the comma to separate the values).
    Open source →
  50. 8.2.0 14 Feb 2024
    Release notes
    • Adds Future<bool> requireWiFi(RequireWiFi requirement, {final rescheduleRunningTasks = true}) to set a globally enforced WiFi requirement, and pause/resume or cancel/restart tasks accordingly. This is helpful when implementing a global toggle switch to prevent data download over metered (cellular) networks. iOS and Android only
    Open source →
  51. 8.1.0 22 Jan 2024
    Release notes
    • Adds responseHeaders to TaskStatusUpdate for tasks that complete successfully (null otherwise). Per Dart convention, header names are lower-cased
    • Added ext.kotlin_version back to build.gradle
    Open source →
  52. 8.0.5 27 Dec 2023
    Release notes

    Android minSdk now 21 (was 24) and compileSdk now 34 (was 33)

    Open source →
  53. 8.0.4 23 Dec 2023
    Release notes

    Kotlin compiler V1.9

    Kotlin compiler version moved from 1.8 to 1.9, typically this means changing your project's build.gradle entry:

    buildscript {
        ext.kotlin_version = '1.9.0' # changed from '1.8.0'
        repositories {
            google()
            mavenCentral()
        }
    

    Enable multiple application instances on Android

    • Changes approach to backgroundChannel and activity fields in Kotlin plugin
    • Allows use of android:launchMode="standard" in Android manifest

    Improvements and bug fixes

    • Notification handling on Android when app is suspended
    • Kotlin code refactoring for posting on backgroundChannel
    • Web compilation
    Open source →
  54. 8.0.3 21 Dec 2023
    Release notes

    Bypassing permissions on iOS

    By default, the downloader allows any of the permissions to be requested, but that also means that Apple requires you to add things like Photo Library Usage Description to your Info.plist, even if you never move files to the Photo Library.

    On iOS, to bypass the permission code altogether at compile time (and therefore remove the need to provide the Info.plist entry) modify your app's Podfile as follows:

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        flutter_additional_ios_build_settings(target)
        
        # The following loop has been added to bypass compilation of specific
        # permissions.
        # If you want to bypass one or more permissions (so that you don't
        # have to include things like a Photo Library Usage Description
        # if you don't add files to the Photo Library) then add this loop
        # and uncomment the permissions you want to bypass.
        # If you bypass (by including the line below) then the
        # check will not happen, and the permission is aways denied. If you
        # bypass you do not need to include the associated entry in your
        # Info.plist file
        target.build_configurations.each do |config|
          config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)']
          #config.build_settings['OTHER_SWIFT_FLAGS'] << '-D BYPASS_PERMISSION_NOTIFICATIONS'
          #config.build_settings['OTHER_SWIFT_FLAGS'] << '-D BYPASS_PERMISSION_IOSADDTOPHOTOLIBRARY'
          #config.build_settings['OTHER_SWIFT_FLAGS'] << '-D BYPASS_PERMISSION_IOSCHANGEPHOTOLIBRARY'
          end
      end
    end
    

    and uncomment the line items that you want to bypass by deleting the # mark at the start of the line.

    Open source →
  55. 8.0.2 11 Dec 2023
    Release notes

    Allow compilation on XCode 14 by adding compiler version gate for Swift >=5.9

    Open source →
  56. 8.0.1 08 Dec 2023
    Release notes

    Fix issue #208 concurrentModificationException on Android and similar in iOS

    Open source →
  57. 8.0.0 07 Dec 2023
    Release notes

    Summary of changes:

    • Permissions must now be explicitly checked and requested to improve user experience and give control to developer
    • Add images and video to iOS Photo Library when using SharedStorage.images or SharedStorage.video
    • SqlitePersistentStorage backing database moved to separate package background_downloader_sql to reduce app size for default
    • Add notification for groups of downloads
    • Add BaseDirectory.root to allow absolute file path (use with care!)
    • Add fields mimeType and charSet to TaskStatusUpdate
    • Add Request.cookieHeader to parse 'Set-Cookie' response header
    • Add platformVersion method
    • Add ready getter to wait for initialization if needed
    • Bug fixes and other improvements

    BREAKING: Permissions

    Permissions are no longer automatically requested. You need to explicitly check, and if necessary ask for permissions ahead of calling methods that use them.

    User permissions may be needed to display notifications, to move files to shared storage (on Android) and to add images or video to the iOS Photo Library. These permissions should be checked and if needed requested before executing those operations.

    You can use a package like permission_handler, or use the FileDownloader().permissions object, which has three methods:

    • status: returns a PermissionsStatus. On Android this is either granted or denied. If you have not asked for permission yet, then Android returns denied and iOS returns .undetermined. iOS can also return .partial
    • request: to request the actual permission. Only do this if you have confirmed that the permission is not already granted
    • shouldShowRationale: for Android only, if true you should show a UI element (e.g. a dialog) to explain to the user why this permission is necessary

    All three methods take one PermissionType parameter:

    • notifications, to display notifications
    • androidSharedStorage, to move files to external storage on Android, before API 29
    • iosAddToPhotoLibrary, to move files to SharedStorage.images or SharedStorage.video on iOS, as this adds those files to the Photo Library
    • iosChangePhotoLibrary, to access the path to files moved to the Photos Library

    For example, to request permissions for notifications:

    final permissionType = PermissionType.notifications;
    var status = await FileDownloader().permissions.status(permissionType);
    if (status != PermissionStatus.granted) {
    if (await FileDownloader().permissions.shouldShowRationale(permissionType)) {
    await showRationaleDialog(permissionType); // Show a dialog with rationale
    }
    status = await FileDownloader().permissions.request(permissionType);
    debugPrint('Permission for $permissionType was $status');
    }
    

    The downloader will check permission status before each action, e.g. will not show notifications unless permissions for notifications have been granted.

    Note that permissions are very platform and version dependent, e.g. notification permissions on Android are only required as of API 33, and iOS 14 introduced new Photo Library permissions. If you want to get into details, you can determine the platform version you're running by calling await FileDownloader().platformVersion().

    BREAKING: Use iOS Photos Library for .video and .images SharedStorage destinations

    Previously, .images and .video destinations were 'faked' on iOS. With this change, when calling moveToSharedStorage, the file is added to the Photos Library (provided the user grants that permission).

    For .images and .video SharedStorage destinations, you need user permission to add to the Photos Library, which requires you to set the NSPhotoLibraryAddUsageDescription key in Info.plist. The returned String is not a filePath, but a unique identifier. If you only want to add the file to the Photos Library you can ignore this identifier. If you want to actually get access to the file (and filePath) in the Photos Library, then the user needs to grant an additional 'modify' permission, which requires you to set the NSPhotoLibraryUsageDescription in Info.plist. To get the actual filePath, call pathInSharedStorage and pass the identifier obtained via the call to moveToSharedStorage as the filePath parameter:

    // assume we have permission
    final identifier = await FileDownloader().moveToSharedStorage(task, SharedStorage.images);
    if (identifier != null) {
      final path = await FileDownloader().pathInSharedStorage(identifier, SharedStorage.images);
      debugPrint('iOS path to dog picture in Photos Library = ${path ?? "permission denied"}');
    } else {
      debugPrint('Could not add file to Photos Library, likely because permission denied');
    }
    

    The reason for this two-step approach is that typically you only want to add to the library (requires PermissionType.iosAddToPhotoLibrary), which does not require the user to give read/write access to their entire photos library (PermissionType.iosChangePhotoLibrary, required to get the filePath).

    BREAKING: PersistentStorage and PersistentStorageMigrator

    If you use the default PersistentStorage then nothing changes. Otherwise:

    • SqlitePersistentStorage moved to a separate package, and the migrator used is SqlPersistentStorageMigrator
    • PersistentStorage is now an interface, not a class, and LocalStorePersistentStorage is the default implementation
    • PersistentStorageMigrator is now an interface, and BasePersistentStorageMigrator is a basic implementation that can be extended to add migration options (as is done in SqlPersistentStorageMigrator)

    Add background_downloader_sql to your dependencies in pubspec.yaml to get SqlitePersistentStorage and SQLite related migration options back.

    The reason for this change is that the sqflite dependency adds significant size to apps, even if they do not use the SQLite functionality.

    Introduce groupNotification

    If you download or upload multiple files simultaneously, you may not want a notification for every task, but one notification representing the group of tasks. To do this, set the groupNotificationId field in a notificationConfig and use that configuration for all tasks in this group. It is easiest to combine this with the group field of the task, e.g.:

    FileDownloader.configureNotificationForGroup('bunchOfFiles',
                running: const TaskNotification(
                    '{numFinished} out of {numTotal}', 'Progress = {progress}'),
                complete:
                    const TaskNotification('Done!', 'Loaded {numTotal} files'),
                error: const TaskNotification(
                    'Error', '{numFailed}/{numTotal} failed'),
                progressBar: true,
                groupNotificationId: 'myGroupNotification');
                
    // start every task like this
    await FileDownloader().enqueue(DownloadTask(
                url: 'https://your_url.com',
                filename: 'your_filename',
                group: 'bunchOfFiles'));
    

    All tasks in group bunchOfFiles will now use the notification group configuration with ID myNotificationGroup.

    Add BaseDirectory.root

    You can now pass an absolute path to the downloader by using BaseDirectory.root combined with the path in directory. This allows you to reach any file destination on your platform. However, be careful: the reason you should not normally do this (and use e.g. BaseDirectory.applicationDocuments instead) is that the location of the app's documents directory may change between application starts (on iOS, and on Android in some cases), and may therefore fail for downloads that complete while the app is suspended. You should therefore never store permanently, or hard-code, an absolute path, unless you are absolutely sure that that path is 'stable'.

    Add fields mimeType and charSet to TaskStatusUpdate

    If the server provides this information via the Content-Type header then these fields will be non-null only for final states.

    Add Request.cookieHeader to parse 'Set-Cookie' response header

    Servers may ask you to set a cookie (via the 'Set-Cookie' header in the response), to be passed along to the next request (in the 'Cookie' header). This may be needed for authentication, or for session state.

    The method Request.cookieHeader makes it easy to insert cookies in a request. The first argument cookies is either a http.Response object (as returned by the FileDownloader().request method), a List<Cookie>, or a String value from a 'Set-Cookie' header. It returns a {'Cookie': '...'} header that can be added to the next request. The second argument is the url you intend to use the cookies with. This is needed to filter the appropriate cookies based on domain and path.

    For example:

    final loginResponse = await FileDownloader()
       .request(Request(url: 'https://server.com/login', headers: {'Auth': 'Token'}));
    const downloadUrl = 'https://server.com/download';
    // add the cookies from the response to the task
    final task = DownloadTask(url: downloadUrl, headers: {
      'Auth': 'Token',
      ...Request.cookieHeader(loginResponse, downloadUrl) // inserts the 'Cookie' header
    });
    

    Add platformVersion method

    Return the platform version as a String:

    • On Android this is the API integer, e.g. "33"
    • On iOS this is the iOS version, e.g. "16.1"
    • On desktop this is a description of the OS version, not parsable

    Add ready

    If initializing a non-default PersistentStorage such as SqlitePersistentStorage you may need to wait for database initialization and perhaps migration to complete before using the downloader. Call await FileDowloader().ready before the first call that involves the persistent storage. Because initialization is often followed immediately by the trackTasks call, that call waits for ready, so this is valid:

    await FileDownloader(persistentStorage: SqlitePersistentStorage()).trackTasks();
    

    Remove awaitGroup

    • Removed all references to awaitGroup as the logic for the convenience methods such as download has changed
    • Removed all references to modifiedTasks in PersistentStorage interface
    • If you use a convenience function, your task must generate status updates (by setting the updates field to Updates.status - the default - or Updates.statusAndProgress)
    • If you use a convenience function and specify a progress callback, your task must also generate status updates (by setting the updates field to Updates.statusAndProgress)

    Bug fixes and other improvements

    • Fixes Pause notification issue on iOS
    • Fixes issue with priority for multi-part file uploads
    • Fixes issue #194: remove notification when canceling a paused task
    • Fixes issue #200: prefer UTF-8 filename in Content-Disposition parse
    • Fixes issue #202: add minimum deployment target to PodSpec on iOS
    • Strip leading path separator from Task.directory instead of throwing an exception
    • Refactors code to improve readability
    Open source →
  58. 7.12.3 10 Nov 2023
    Release notes

    Issue #189 related to resume on Android versions prior to S, and to expediting a task prior to S

    See https://stackoverflow.com/a/68468786/4172761

    Fixes issue with parsing priority from JSON Fixes issue with setting expedited for Android versions prior to S. This effectively ignores priority (expedited) scheduling for tasks prior to Android S and defaults to normal.

    Open source →
  59. 7.12.2 23 Oct 2023
    Release notes

    Minor improvements to TaskQueue and MemoryTaskQueue

    Open source →
  60. 7.12.1 22 Oct 2023
    Release notes

    Bug fix for web compilation

    Open source →

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive