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 2026Releases
latest 60 of 133-
9.5.813 Aug 2026Release notes
Open source →- Add
alsoRestartUploadsparameter toFileDownloader.requireWiFito optionally cancel and restart running upload tasks when updating WiFi requirement (fixes #695) - [iOS] Fixes typo in
PrivacyInfo.xcprivacyfor photos and videos data collection key (NSPrivacyCollectedDataTypePhotosorVideos), resolving App Store submission validation errors (fixes #694) - [Documentation] Adds comprehensive documentation for
TaskStatusUpdateandTaskProgressUpdateindoc/status_updates.md(fixes #684)
- Add
-
9.5.727 Jul 2026Release notes
Open source →- [Android] Bug fixes:
- Fixes group notification sticking in running state when cancelling tasks from holding queue
- Fixes
ConcurrentModificationExceptionincanSendCancellationwhen 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_ACCESSrace condition under concurrent status updates
- [General] Defer
JsonProcessoridle shutdown while requests are pending to prevent unexpected background isolate shutdown errors
Release notes
Open source →- [Android] Bug fixes:
- Fixes group notification sticking in running state when cancelling tasks from holding queue
- Fixes
ConcurrentModificationExceptionincanSendCancellationwhen 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_ACCESSrace condition under concurrent status updates
- [General] Defer
JsonProcessoridle shutdown while requests are pending to prevent unexpected background isolate shutdown errors
- [Android] Bug fixes:
-
9.5.612 Jul 2026Release notes
Open source →- [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-androidplugin in Gradle build ifbuiltInKotlinis configured tofalse(closes #674)
- [iOS] Swift Package Manager (SPM) and build fixes:
- Resolves package dependency resolution errors when using SPM by removing invalid
FlutterFrameworkdependency path in Package.swift - Restores compatibility with older Flutter SDKs in the example app's
AppDelegate.swift
- Resolves package dependency resolution errors when using SPM by removing invalid
- [Chore / Maintenance] Updated GitHub Actions workflow configurations to resolve Node 20 deprecation warnings
Release notes
Open source →- [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-androidplugin in Gradle build ifbuiltInKotlinis configured tofalse(closes #674)
- [iOS] Swift Package Manager (SPM) and build fixes:
- Resolves package dependency resolution errors when using SPM by removing invalid
FlutterFrameworkdependency path in Package.swift - Restores compatibility with older Flutter SDKs in the example app's
AppDelegate.swift
- Resolves package dependency resolution errors when using SPM by removing invalid
- [Chore / Maintenance] Updated GitHub Actions workflow configurations to resolve Node 20 deprecation warnings
- [Android] Bug fixes and build improvements:
-
9.5.527 May 2026Release notes
Open source →- [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
Release notes
Open source →- [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
- [Android] Bug fixes and performance improvements:
-
9.5.427 Feb 2026Release notes
Open source →- Add
TaskOptionstoDataTaskwith similar functionality as inDownloadTask - Bug fixes
Release notes
Open source →- Add
TaskOptionstoDataTaskwith similar functionality as inDownloadTask - Bug fixes
- Add
-
9.5.323 Feb 2026Release notes
Open source →- [Android] Fix race condition in
TaskWorkerwheresetForegroundcould crash for finished tasks - Add documentation
- Add integration testing documentation and steps
- [Android] Fix race condition in
-
9.5.214 Jan 2026Release notes
Open source →- Moves LocalStorePersistentStorage to isolate model to off-load i/o operations, and unwinds Future.wait to loop
-
9.5.109 Jan 2026 -
9.5.031 Dec 2025Release notes
Open source →- 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
allowPauseis 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. RequiresFOREGROUND_SERVICE_DATA_SYNCpermission on Android 14+. - User Initiated Data Transfer (UIDT): Triggered by setting
priority: 0on 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_JOBSpermission to yourAndroidManifest.xml, as well as a service declaration for.UIDTJobService. See the README for details.
- Note: Using UIDT requires adding the
- 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
- [Android] Fixes
HoldingQueuelogic 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
LocalStorePersistentStorageto prevent UI stutter - [General] Documentation refactor: broken down README into separate files in
doc/directory
Release notes
Open source →- 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
allowPauseis 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. RequiresFOREGROUND_SERVICE_DATA_SYNCpermission on Android 14+. - User Initiated Data Transfer (UIDT): Triggered by setting
priority: 0on 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_JOBSpermission to yourAndroidManifest.xml, as well as a service declaration for.UIDTJobService. See the README for details.
- Note: Using UIDT requires adding the
- 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
- [Android] Fixes
HoldingQueuelogic 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
LocalStorePersistentStorageto prevent UI stutter - [General] Documentation refactor: broken down README into separate files in
doc/directory
- 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:
-
9.4.428 Dec 2025Release notes
Open source →- [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.splitandParallelDownloadTaskpause
Release notes
Open source →- [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.splitandParallelDownloadTaskpause
-
9.4.321 Dec 2025Release notes
Open source →- Improve JSON processing to reduce UI jank
- Optimize database operations
- Add
database.cleanUp()to simplify and automate database maintenance, and added database clean-up tostart()command - Fix iOS background callback performance
Release notes
Open source →- Improve JSON processing to reduce UI jank
- Optimize database operations
- Add
database.cleanUp()to simplify and automate database maintenance, and added database clean-up tostart()command - Fix iOS background callback performance
-
9.4.218 Dec 2025Release notes
Open source →- Improve Android notification handling
- Add local test server to reduce test suite flakiness
- Improve code documentation
-
9.4.110 Dec 2025Release notes
Open source →- Refine TaskQueue pauseAll/resumeAll to support specific tasks and groups. Updated
TaskQueueinterface to accept optionaltasksandgroupparameters inpauseAllandresumeAlland implemented this for theMemoryTaskQueue - Change platform checks from
Platform.isAndroidetc todefaultTargetPlatformwhich should lead to better tree shaking and smaller memory size
- Refine TaskQueue pauseAll/resumeAll to support specific tasks and groups. Updated
-
9.4.021 Nov 2025Release notes
Open source →- 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.skipExistingFilesconfiguration option to skip the download if the destination file already exists (and conditionally does this only for files greater than a certain size)
-
9.3.002 Nov 2025Release notes
Open source →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
pauseAllandresumeAll. These methods are now called when pausing or resuming all tasks using theFileDownloader
- Add pause and resume functionality to TaskQueue using
-
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
-
-
9.2.620 Sep 2025Release notes
Open source →-
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
-
-
9.2.513 Sep 2025Release notes
Open source →- Minor bug fixes
- [iOS] catch file write errors in multi-part uploads
- [iOS] fix optional unwrap error
- Minor bug fixes
-
9.2.411 Sep 2025Release notes
Open source →- Minor bug fixes
- [iOS] Notification update after killed app resumes
- [Android] Use of UTF-8 characters in
postfield - Multipart upload remove extraneous CR/LF after header
- [Android] Fallback for expedited tasks when encountering OS error
- [Android] fix
enqueAlland similar calls when no Activity attached
- Minor bug fixes
-
9.2.319 Jul 2025Release notes
Open source →- 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
- Minor bug fixes
-
9.2.217 May 2025Release notes
Open source →- Adds option to set the
Content-Dispositionheader for binary uploads - Fixes bug on iOS when using
openFilewith a filepath containing spaces
For binary uploads, the
Content-Dispositionheader 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
- Adds option to set the
-
9.2.127 Apr 2025 -
9.2.015 Mar 2025Release notes
Open source →- Adds
updatesbroadcast stream to thedatabaseproperty, which emits everyTaskRecordupdate made to the database. - Fixes issue with
startandrescheduleKilledTaskswhen using group names for tasks - Minor bug fixes and enhancements
- Adds
-
9.1.105 Mar 2025Release notes
Open source →- Adds optional
groupparameter toresumeAll - Fixes concurrency bugs in Android
- Adds optional
-
9.1.002 Mar 2025Release notes
Open source →- Adds
pauseAll,cancelAllandresumeAll - Improves handling
enqueuAllto not starve the UI thread - Fixes compilation bug when compiling with XCode 16.2
- Adds
-
9.0.023 Feb 2025Release notes
Open source →- Introduces URI operations, including file/photo/video/directory pickers, under the
uriproperty, andUriDownloadTaskandUriUploadTask. See Working with URIs. - [Breaking] removes references to
asAndroidUri- use the new methods on theuriproperty instead - Adds
enqueueAllto enqueue a list of tasks (non-blocking and with better performance when compared to callingenqueuefor each task) - Adds native callback
beforeTaskStartCallbacktoTaskOptions, that is called just before the task starts executing. If it returns a non-nullTaskStatusUpdatethen this forces the task to finish before it starts, with thatTaskStatusUpdate - Adds option to configure a
cancelednotification (instead of cancellation defaulting to theerrornotification) - Fixes bug with
requestwhen usingPUTorPATCHrequests - 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().uriproperty 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 aUri, aFileor aStringcontaining a file pathmoveFile(): moves a file from a source uri to a destination. Destination can be aUri, aFileor aStringcontaining a file path. If the move fails, it is possible that the file was copied but the source was not deleteddeleteFile(): 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 usepersistedUriPermissionor use the photo/video picker.
The
pick...methods andcreateDirectorytake an optionalpersistedUriPermissionargument (defaults tofalse) that whentrueregisters the picked directory with the OS, allowing access in a later session.New/modified
Tasktypes: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 thefilenameis omitted, it will be based on the task's URL.MultiUploadTask: now accepts Uri where previously only filename or file path was allowed
- Introduces URI operations, including file/photo/video/directory pickers, under the
-
8.9.515 Mar 2025Release notes
Open source →- Fixes issue with
startandrescheduleKilledTaskswhen using group names for tasks
- Fixes issue with
-
8.9.425 Jan 2025Release notes
Open source →- Modifies the interval between
TaskProgressUpdatesuch 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
rescheduleKilledTasksto also reschedule tasks marked aswaitingToRetrybut not registered as such
- Modifies the interval between
-
8.9.318 Jan 2025Release notes
Open source →- Adds
startwhich ensures the various start-up calls are executed in the correct order. Use this instead of callingtrackTasks,resumeFromBackgroundandrescheduleKilledTasksseparately - Adds
rescheduleKilledTaskswhich 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)
- Adds
-
8.9.216 Jan 2025Release notes
Open source →- Adds
rescheduleMissingTasks, which compares tasks in thedatabasewith 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 ofTask- 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
- Adds
-
8.9.112 Jan 2025Release notes
Open source →- [iOS] Adds Privacy Manifest
- [iOS] Adds support for Swift Package Manager and defaults the example app to using it
-
8.9.010 Jan 2025Release notes
Open source →- Adds
optionsfield to Task, which take aTaskOptionsobject to configure less common task specific options - currentlyonTaskStart,onTaskFinishedandauthonTaskStartis a callback with signatureFuture<Task?> Function(Task original), called just before the task starts executing. Your callback receives theoriginaltask 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'surl(including query parameters) andheadersproperties - making changes to any other property may lead to undefined behavior.onTaskFinishedis a callback with signatureFuture<void> Function(TaskStatusUpdate taskStatusUpdate), called when the task has reached a final state (regardless of outcome). Your callback receives the finalTaskStatusUpdateand can act on that.authis an optionalAuthobject 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
numTotalis calculated for group notifications:numTotalis 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)
- Adds
-
8.8.118 Dec 2024 -
8.8.010 Nov 2024Release notes
Open source →- [iOS] Adds configuration option to exclude downloaded files from iCloud backup
- Adds
allGroupsparameter toallTasksandallTaskIdsmethods, to retrieve all tasks regardless ofgroup - [Android] Fixes issue with un-commanded restart of a download in specific scenarios
-
8.7.108 Nov 2024 -
8.7.003 Nov 2024Release notes
Open source →- 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 methodsmoveToSharedStorageandpathInSharedStorageby addingasAndroidUri = trueto 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_overridesin pubspec.yaml to resolve (background_downloader works with 1.0 and 2.0)
- Adds option to specify a file location for upload using a Mediastore URI on Android, using
-
8.6.024 Oct 2024Release notes
Open source →- 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)
- Adds option for partial uploads, for binary uploads only. Set the byte range by adding a "Range" header to your binary
-
8.5.627 Sep 2024Release notes
Open source →- 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
-
8.5.505 Sep 2024 -
8.5.402 Sep 2024Release notes
Open source →- If the value of a
fieldsentry of anUploadTaskis in JSON format (defined as start/end with {} or []) then the field's mime-type will be set toapplication/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 withTask.splitandTask.baseDirectoryPath. When usingBaseDirectory.rooton Windows, your task'sdirectorymust contain the drive letter.
- If the value of a
-
8.5.309 Aug 2024 -
8.5.219 May 2024Release notes
Open source →- Removes references to
dart:htmlto allow web compilation using WASM. Note the package still does not work on the web - Adds auto-decode of
postfield if Map or List. Throws ifjsonEncodecannot convert the object, in which case you have to encode it yourself using a custom encoder
- Removes references to
-
8.5.115 May 2024Release notes
Open source →- Fixes an issue where temporary files were not deleted when canceling a paused parallel download task
-
8.5.010 May 2024Release notes
Open source →- Adds
DataTaskfor 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 otherTask.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. ADataTaskis similar to aDownloadTaskexcept it:- Does not accept file information, as there is no file involved
- Does not allow progress updates
- Accepts
postdata as a String, or - Accepts
jsondata, which will be converted to a String and posted as content typeapplication/json - Accepts
contentTypewhich will set theContent-Typeheader value - Returns the server
responseBody,responseHeadersand possibletaskExceptionin the finalTaskStatusUpdatefields
Typically you would use
enqueueto enqueue aDataTaskand monitor the result using a listener or callback, but you can also usetransmitto enqueue and wait for the final result of theDataTask. - Adds
-
8.4.312 Apr 2024Release notes
Open source →- Fixes iOS/Android issue where
retrieveLocallyStoredDataretrieves only a basicTaskStatusUpdate, without responseCode, responseBody etc
- Fixes iOS/Android issue where
-
8.4.208 Apr 2024Release notes
Open source →- Fixes iOS/Android bug with ParallelDownloadTask hanging when number of chunks exceeds ~10
-
8.4.120 Mar 2024Release notes
Open source →- Fixes Android bug when using
Config.runInForegroundthat can lead to a crash
- Fixes Android bug when using
-
8.4.017 Mar 2024Release notes
Open source →- Adds optional holding queue to manage how many tasks are executed concurrently
- Fixes bug with using
uniqueparameter in context of server suggested filename - Transition from imperative to declarative Gradle plugin application, see here
Holding queue
Once you
enqueuea task with theFileDownloaderit 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 aTaskQueueor aHoldingQueue:- A
TaskQueueis a Dart object that you can add to theFileDownloader. You can create this object yourself (implementing theTaskQueueinterface) or use the bundledMemoryTaskQueueimplementation. This queue sits "in front of" theFileDownloaderand instead of using theenqueueanddownloadmethods directly, you now simplyaddyour tasks to theTaskQueue. 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 theTaskQueuewill be lost (unless you have implemented persistence) - A
HoldingQueueis native to the OS and can be configured usingFileDownloader().configureto 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 constraintsmaxConcurrentto 3,maxConcurrentByHostto 2, andmaxConcurrentByGroupto 1. Passnullfor 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 OSWhen 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
maxConcurrentvalues - Status messages will differ slightly. You will get the
TaskStatus.enqueuedupdate 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 getTaskStatus.runningas 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
taskForIdandcancelTasksWithIds. There is no way to determine whether a task is in the holding queue or already enqueued with the Android WorkManager or iOS URLSessions
-
8.3.011 Mar 2024Release notes
Open source →- Adds
responseStatusCodetoTaskStatusUpdatefor tasks that result inTaskStatus.completeorTaskStatus.notFound(null otherwise). - Adds
Task.splitto extract the baseDirectory, directory and filename from an absolute filePath or a File. This is saver than using.fromFileand preferred - Adds
UploadTask.fromFileto create anUploadTaskfrom an existingFileobject. Note that this will create a task with an absolute path reference andBaseDirectory.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
Fileobject, you can extract the values forbaseDirectory,directoryandfilenameusingTask.splitto 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.xmla permission declaration<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />and the foreground service type definition (under theapplicationelement):<service android:name="androidx.work.impl.foreground.SystemForegroundService" android:foregroundServiceType="dataSync" tools:node="merge" /> - Adds
-
8.2.121 Feb 2024Release notes
Open source →- Adds option to specify multiple values for a single field name in the
UploadTask.fieldsproperty by formatting the value as'"value1", "value2", "value3"'(note the double quotes and the comma to separate the values).
- Adds option to specify multiple values for a single field name in the
-
8.2.014 Feb 2024Release notes
Open source →- 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
- Adds
-
8.1.022 Jan 2024Release notes
Open source →- Adds
responseHeaderstoTaskStatusUpdatefor tasks that complete successfully (null otherwise). Per Dart convention, header names are lower-cased - Added
ext.kotlin_versionback to build.gradle
- Adds
-
8.0.527 Dec 2023 -
8.0.423 Dec 2023Release notes
Open source →Kotlin compiler V1.9
Kotlin compiler version moved from 1.8 to 1.9, typically this means changing your project's
build.gradleentry: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
-
8.0.321 Dec 2023Release notes
Open source →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 endand uncomment the line items that you want to bypass by deleting the
#mark at the start of the line. -
8.0.211 Dec 2023Release notes
Open source →Allow compilation on XCode 14 by adding compiler version gate for Swift >=5.9
-
8.0.108 Dec 2023Release notes
Open source →Fix issue #208 concurrentModificationException on Android and similar in iOS
-
8.0.007 Dec 2023Release notes
Open source →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.imagesorSharedStorage.video SqlitePersistentStoragebacking database moved to separate packagebackground_downloader_sqlto reduce app size for default- Add notification for groups of downloads
- Add
BaseDirectory.rootto allow absolute file path (use with care!) - Add fields
mimeTypeandcharSettoTaskStatusUpdate - Add
Request.cookieHeaderto parse 'Set-Cookie' response header - Add
platformVersionmethod - Add
readygetter 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().permissionsobject, which has three methods:status: returns aPermissionsStatus. On Android this is eithergrantedordenied. If you have not asked for permission yet, then Android returnsdeniedand iOS returns.undetermined. iOS can also return.partialrequest: to request the actual permission. Only do this if you have confirmed that the permission is not alreadygrantedshouldShowRationale: for Android only, iftrueyou should show a UI element (e.g. a dialog) to explain to the user why this permission is necessary
All three methods take one
PermissionTypeparameter:notifications, to display notificationsandroidSharedStorage, to move files to external storage on Android, before API 29iosAddToPhotoLibrary, to move files toSharedStorage.imagesorSharedStorage.videoon iOS, as this adds those files to the Photo LibraryiosChangePhotoLibrary, 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
.imagesand.videoSharedStorage destinations, you need user permission to add to the Photos Library, which requires you to set theNSPhotoLibraryAddUsageDescriptionkey inInfo.plist. The returned String is not afilePath, 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 (andfilePath) in the Photos Library, then the user needs to grant an additional 'modify' permission, which requires you to set theNSPhotoLibraryUsageDescriptioninInfo.plist. To get the actualfilePath, callpathInSharedStorageand pass the identifier obtained via the call tomoveToSharedStorageas thefilePathparameter:// 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 thefilePath).BREAKING: PersistentStorage and PersistentStorageMigrator
If you use the default
PersistentStoragethen nothing changes. Otherwise:SqlitePersistentStoragemoved to a separate package, and the migrator used isSqlPersistentStorageMigratorPersistentStorageis now an interface, not a class, andLocalStorePersistentStorageis the default implementationPersistentStorageMigratoris now an interface, andBasePersistentStorageMigratoris a basic implementation that can be extended to add migration options (as is done inSqlPersistentStorageMigrator)
Add
background_downloader_sqlto your dependencies in pubspec.yaml to getSqlitePersistentStorageand SQLite related migration options back.The reason for this change is that the
sqflitedependency 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
groupNotificationIdfield in anotificationConfigand use that configuration for all tasks in this group. It is easiest to combine this with thegroupfield 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
bunchOfFileswill now use the notification group configuration with IDmyNotificationGroup.Add
BaseDirectory.rootYou can now pass an absolute path to the downloader by using
BaseDirectory.rootcombined with the path indirectory. 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.applicationDocumentsinstead) 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
mimeTypeandcharSettoTaskStatusUpdateIf the server provides this information via the
Content-Typeheader 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.cookieHeadermakes it easy to insert cookies in a request. The first argumentcookiesis either ahttp.Responseobject (as returned by theFileDownloader().requestmethod), aList<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 theurlyou 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
readyIf initializing a non-default
PersistentStoragesuch asSqlitePersistentStorageyou may need to wait for database initialization and perhaps migration to complete before using the downloader. Callawait FileDowloader().readybefore the first call that involves the persistent storage. Because initialization is often followed immediately by thetrackTaskscall, that call waits forready, so this is valid:await FileDownloader(persistentStorage: SqlitePersistentStorage()).trackTasks();Remove
awaitGroup- Removed all references to
awaitGroupas the logic for the convenience methods such asdownloadhas changed - Removed all references to
modifiedTasksinPersistentStorageinterface - If you use a convenience function, your task must generate status updates (by setting the
updatesfield toUpdates.status- the default - orUpdates.statusAndProgress) - If you use a convenience function and specify a progress callback, your task must also generate status updates (by setting the
updatesfield toUpdates.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
-
7.12.310 Nov 2023Release notes
Open source →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.
-
7.12.223 Oct 2023 -
7.12.122 Oct 2023