The downloader can be configured by calling FileDownloader().configure
before executing any downloads or uploads. Configurations can be set for Global, Android, iOS or Desktop separately, where only the globalConfig
is applied to every platform, before the platform-specific configuration is applied. This can be used to 'override' the configuration for only one platform.
Configurations are platform-specific and support and behavior may change between platforms. At this moment, consider configuration experimental, and expect changes that will not be considered breaking (and will not trigger a major version increase).
A configuration can be a single config or a list of configs, and every config is a Record
with the first element a String
indicating what to configure (use the Config.
variables to avoid typos), and the second element an argument (which itself can be a Record
if more than one argument is needed).
The following configurations are supported:
-
Timeouts
(Config.requestTimeout, Duration? duration)
sets the requestTimeout, or if null resets to default. This is the time allowed to connect with the server(Config.resourceTimeout, Duration? duration)
sets the iOS resourceTimeout, or if null resets to default. This is the time allowed to complete the download/upload
-
Checking available space
(Config.checkAvailableSpace, int minMegabytes)
ensures a file download fails if less thanminMegabytes
space will be available after this download completes(Config.checkAvailableSpace, false)
or(Config.checkAvailableSpace, Config.never)
turns off checking available space
-
Using a holding queue to limit the number of tasks running concurrently
(Config.holdingQueue, (int? maxConcurrent, int? maxConcurrentByHost, int? maxConcurrentByGroup))
activates the holding queue and sets the constraints. Passnull
for no constraint- 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 getTaskStatus.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
andcancelTasksWithIds
. There is no way to determine whether a task is in the holding queue or already enqueued with the Android WorkManager or iOS URLSessions
- Tasks will be taken out of the queue based on their priority and time of creation, provided they pass the constraints imposed by the
-
[Android] When to use the cache directory
(Config.useCacheDir, String whenToUse)
with values 'never', 'always' or 'whenAble'. Default isConfig.whenAble
, which will use the cacheDir if the size of the file to download is less than half the cacheQuota given to your app by Android. If you find that your app fails to download large files or cannot resume from pause, set this toConfig.never
and make sure to clear up the directory aligned withBaseDirectory.applicationSupport
for stray temp files. Temp file names start withcom.bbflight.background_downloader
. Note that the use of cache or applicationSupport directories responds to the configurationConfig.useExternalStorage
: if set, the external cache and applicationSupport directories will be used
-
HTTP Proxy
(Config.proxy, (String address, int port))
sets the proxy to this address and port (note: address and port are contained in a record)(Config.proxy, false)
removes the proxy
-
[Android, Desktop] Bypassing HTTPS (TLS) certificate validation
(Config.bypassTLSCertificateValidation, bool bypass)
bypasses TLS certificate validation for HTTPS connections. This is insecure, and can not be used in release mode. It is meant to make it easier to use a local server with a self-signed certificate during development only. On Android, to turn the bypass off, restart your app with this configuration removed.
-
[Android] run task in foreground (removes 9 minute timeout and may improve chances of task surviving background).
For a task to run in foreground it must have a
running
notification configured, otherwise it will execute normally regardless of this setting. If targeting API 34 or greater, you must also add to yourAndroidManifest.xml
a permission declaration<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
and the foreground service type definition (under theapplication
element):<service android:name="androidx.work.impl.foreground.SystemForegroundService" android:foregroundServiceType="dataSync" />
(Config.runInForeground, bool activate)
or(Config.runInForeground, Config.always)
or(Config.runInForeground, Config.never)
activates or de-activates foreground mode for all tasks(Config.runInForegroundIfFileLargerThan, int fileSize)
activates foreground mode for downloads/uploads that exceed this file size, expressed in MB
-
[Android] Use external storage.
Either your app runs in default (internal storage) mode, or in external storage. You cannot switch between internal and external, as the directory structure that - for example -
BaseDirectory.applicationDocuments
refers to is different in each mode(Config.useExternalStorage, String whenToUse)
with values 'never' or 'always'. Default isConfig.never
. See here for important details
-
[iOS] Localization
(Config.localize, Map<String, String> translation)
localizes the words 'Cancel', 'Pause' and 'Resume' as used in notifications, presented as a map (iOS only, see docs for Android notifications)
-
[iOS] Exclude downloaded file from iCloud backup
(Config.excludeFromCloudBackup, bool exclude)
or(Config.excludeFromCloudBackup, Config.always)
or(Config.excludeFromCloudBackup, Config.never)
On Android and iOS, most configurations are stored in native 'shared preferences' to ensure that background tasks have access to the configuration. This means that configuration persists across application restarts, and this can lead to some surprising results. For example, if during testing you set a proxy and then remove that configuration line, the proxy configuration is not removed from persistent storage on your test device. You need to explicitly set ('proxy', false)
to remove the stored configuration on that device.
A configuration can be called multiple times, and affects all tasks running after the configuration call. Tasks enqueued before a call, that run after the call (e.g. because they are waiting for other downloads to complete) will run under the newly set configuration, not the one that was active when they were enqueued. On iOS, configuration of requestTimeout, resourceTimeout and proxy can only be set once, before the first task is executed
Android has a complex storage model, see here, that allows you to store App-Specific files in internal or external storage, and also offers Shared Storage. For Shared Storage you can use FileDownloader().moveToSharedStorage
- this does not require configuration, and won't be covered here.
For App-specific storage you can configure the downloader to use either internal storage (the default) or external storage. Unlike other configurations, you cannot switch, so you have to choose which mode your app will use.
The configuration affects the path to the directories described by the BaseDirectory
enum. For internal storage, the paths will look like this:
- BaseDirectory.applicationDocuments -> path is /data/user/0/com.bbflight.background_downloader_example/app_flutter/google.html
- BaseDirectory.applicationSupport -> path is /data/user/0/com.bbflight.background_downloader_example/files/google.html
- BaseDirectory.applicationLibrary -> path is /data/user/0/com.bbflight.background_downloader_example/files/Library/google.html
- BaseDirectory.temporary -> path is /data/user/0/com.bbflight.background_downloader_example/cache/google.html
Despite the somewhat strange app_flutter
subdirectory, these paths line up with the directories you will get when using the path_provider
package.
When configuring the downloader to use external storage, those same BaseDirectory
entries will create a path like this:
- BaseDirectory.applicationDocuments -> path is /storage/emulated/0/Android/data/com.bbflight.background_downloader_example/files/google.html
- BaseDirectory.applicationSupport -> path is /storage/emulated/0/Android/data/com.bbflight.background_downloader_example/files/Support/google.html
- BaseDirectory.applicationLibrary -> path is /storage/emulated/0/Android/data/com.bbflight.background_downloader_example/files/Library/google.html
- BaseDirectory.temporary -> path is /storage/emulated/0/Android/data/com.bbflight.background_downloader_example/cache/google.html
Note the external files
or cache
directory is now a base, with subdirectories for 'Support' and 'Library'. Calls to task.filePath
will return the correct path to these external directories - they cannot easily be constructed otherwise.
Never store absolute paths to these files, as the actual location may differ. Also note that using external storage can lead to errors, as the storage may not be available at the time it is requested. Therefore, task.filePath
may throw a FileSystemException
if using external storage.