flutter_downloader 1.8.0+1 icon indicating copy to clipboard operation
flutter_downloader: ^1.8.0+1 copied to clipboard

A plugin for creating and managing download tasks. Supports iOS and Android.

flutter_community

Flutter Downloader #

flutter_downloader on pub.dev

A plugin for creating and managing download tasks. Supports iOS and Android.

This plugin is using WorkManager on Android and NSURLSessionDownloadTask on iOS to run download tasks in background.

Development note: #

The changes of external storage APIs in Android 11 cause some problems with the current implementation. I decide to re-design this plugin with new strategy to manage download file location. It is still in triage and discussion in this PR. It is very appreciated to have contribution and feedback from Flutter developer to get better design for the plugin.

iOS integration #

Required configuration: #

The following steps require to open your ios project in Xcode.

  1. Enable background mode.
  1. Add sqlite library.

  1. Configure AppDelegate:

Objective-C:

/// AppDelegate.h
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>

@interface AppDelegate : FlutterAppDelegate

@end
// AppDelegate.m
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#include "FlutterDownloaderPlugin.h"

@implementation AppDelegate

void registerPlugins(NSObject<FlutterPluginRegistry>* registry) {
  if (![registry hasPlugin:@"FlutterDownloaderPlugin"]) {
     [FlutterDownloaderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterDownloaderPlugin"]];
  }
}

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  [FlutterDownloaderPlugin setPluginRegistrantCallback:registerPlugins];
  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

Or Swift:

import UIKit
import Flutter
import flutter_downloader

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

private func registerPlugins(registry: FlutterPluginRegistry) {
    if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
       FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
    }
}

Optional configuration: #

  • Support HTTP request: if you want to download file with HTTP request, you need to disable Apple Transport Security (ATS) feature. There're two options:
  1. Disable ATS for a specific domain only: (add following codes to your Info.plist file)
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>www.yourserver.com</key>
    <dict>
      <!-- add this key to enable subdomains such as sub.yourserver.com -->
      <key>NSIncludesSubdomains</key>
      <true/>
      <!-- add this key to allow standard HTTP requests, thus negating the ATS -->
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <!-- add this key to specify the minimum TLS version to accept -->
      <key>NSTemporaryExceptionMinimumTLSVersion</key>
      <string>TLSv1.1</string>
    </dict>
  </dict>
</dict>
  1. Completely disable ATS: (add following codes to your Info.plist file)
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key><true/>
</dict>
  • Configure maximum number of concurrent tasks: the plugin allows 3 download tasks running at a moment by default (if you enqueue more than 3 tasks, there're only 3 tasks running, other tasks are put in pending state). You can change this number by adding following codes to your Info.plist file.
<!-- changes this number to configure the maximum number of concurrent tasks -->
<key>FDMaximumConcurrentTasks</key>
<integer>5</integer>
  • Localize notification messages: the plugin will send a notification message to notify user in case all files are downloaded while your application is not running in foreground. This message is English by default. You can localize this message by adding and localizing following message in Info.plist file. (you can find the detail of Info.plist localization in this link)
<key>FDAllFilesDownloadedMessage</key>
<string>All files have been downloaded</string>

Note:

  • This plugin only supports save files in NSDocumentDirectory

Android integration #

There's no additional configurations required on Android.

In order to handle click action on notification to open the downloaded file on Android, you need to add some additional configurations.

Add the following to AndroidManifest.xml:

<provider
    android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
    android:authorities="${applicationId}.flutter_downloader.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

Note:

  • You have to save your downloaded files in external storage (where the other applications have permission to read your files)
  • The downloaded files are only able to be opened if your device has at least an application that can read these file types (mp3, pdf, etc)

Optional configuration: #

  • Configure maximum number of concurrent tasks: the plugin depends on WorkManager library and WorkManager depends on the number of available processor to configure the maximum number of tasks running at a moment. You can setup a fixed number for this configuration by adding following codes to your AndroidManifest.xml:
<!-- Begin FlutterDownloader customization -->
<!-- disable default Initializer -->
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>

<!-- declare customized Initializer -->
<provider
    android:name="vn.hunghd.flutterdownloader.FlutterDownloaderInitializer"
    android:authorities="${applicationId}.flutter-downloader-init"
    android:exported="false">
    <!-- changes this number to configure the maximum number of concurrent tasks -->
    <meta-data
        android:name="vn.hunghd.flutterdownloader.MAX_CONCURRENT_TASKS"
        android:value="5" />
</provider>
<!-- End FlutterDownloader customization -->
  • Localize notification messages: you can localize notification messages of download progress by localizing following messages. (you can find the detail of string localization in Android in this link)
<string name="flutter_downloader_notification_started">Download started</string>
<string name="flutter_downloader_notification_in_progress">Download in progress</string>
<string name="flutter_downloader_notification_canceled">Download canceled</string>
<string name="flutter_downloader_notification_failed">Download failed</string>
<string name="flutter_downloader_notification_complete">Download complete</string>
<string name="flutter_downloader_notification_paused">Download paused</string>
  • PackageInstaller: in order to open APK files, your application needs REQUEST_INSTALL_PACKAGES permission. Add the following code in your AndroidManifest.xml:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

See also:

Usage #

Import and initialize #

import 'package:flutter_downloader/flutter_downloader.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Plugin must be initialized before using
  await FlutterDownloader.initialize(
    debug: true // optional: set to false to disable printing logs to console (default: true)
    ignoreSsl: true // option: set to false to disable working with http links (default: false)
  );

  runApp(/*...*/)
}

Create new download task #

final taskId = await FlutterDownloader.enqueue(
  url: 'your download link',
  savedDir: 'the path of directory where you want to save downloaded files',
  showNotification: true, // show download progress in status bar (for Android)
  openFileFromNotification: true, // click on notification to open downloaded file (for Android)
);

Update download progress #

FlutterDownloader.registerCallback(callback); // callback is a top-level or static function

Important

UI is rendered on the main isolate, while download events come from the background isolate (in other words, code in callback is run in the background isolate), so you have to handle the communication between two isolates. For example:

ReceivePort _port = ReceivePort();

@override
void initState() {
  super.initState();

  IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
  _port.listen((dynamic data) {
    String id = data[0];
    DownloadTaskStatus status = data[1];
    int progress = data[2];
    setState((){ });
  });

  FlutterDownloader.registerCallback(downloadCallback);
}

@override
void dispose() {
  IsolateNameServer.removePortNameMapping('downloader_send_port');
  super.dispose();
}

@pragma('vm:entry-point')
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
  final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port');
  send.send([id, status, progress]);
}

@pragma('vm:entry-point') must be placed above the callback function to avoid tree shaking in release mode for Android.

Load all download tasks #

final tasks = await FlutterDownloader.loadTasks();

Load download tasks using a raw SQL query #

final tasks = await FlutterDownloader.loadTasksWithRawQuery(query: query);

In order to parse data into DownloadTask object successfully, you should load data with all fields from the database (in the other words, use SELECT * ). For example:

SELECT * FROM task WHERE status=3

Below is the schema of the task table where flutter_downloader plugin stores information about download tasks

CREATE TABLE `task` (
  `id`  INTEGER PRIMARY KEY AUTOINCREMENT,
  `task_id` VARCHAR ( 256 ),
  `url` TEXT,
  `status`  INTEGER DEFAULT 0,
  `progress`  INTEGER DEFAULT 0,
  `file_name` TEXT,
  `saved_dir` TEXT,
  `resumable` TINYINT DEFAULT 0,
  `headers` TEXT,
  `show_notification` TINYINT DEFAULT 0,
  `open_file_from_notification` TINYINT DEFAULT 0,
  `time_created`  INTEGER DEFAULT 0
);

Cancel a task #

FlutterDownloader.cancel(taskId: taskId);

Cancel all tasks #

FlutterDownloader.cancelAll();

Pause a task #

FlutterDownloader.pause(taskId: taskId);

Resume a task #

FlutterDownloader.resume(taskId: taskId);

resume() will return a new taskId corresponding to a new background task that is created to continue the download process. You should replace the old taskId (that has paused status) by the new taskId to continue tracking the download progress.

Retry a failed task #

FlutterDownloader.retry(taskId: taskId);

retry() will return a new taskId (just like resume())

Remove a task #

FlutterDownloader.remove(taskId: taskId, shouldDeleteContent:false);

Open and preview a downloaded file #

FlutterDownloader.open(taskId: taskId);

On Android, you can only open a downloaded file if it is placed in the external storage and there's at least one application that can read that file type on your device.

Bugs/Requests #

Feel free to open an issue if you encounter any problems or think that the plugin is missing some feature.

Pull request are also very welcome!

874
likes
130
pub points
99%
popularity

Publisher

verified publisher iconfluttercommunity.dev

A plugin for creating and managing download tasks. Supports iOS and Android.

Repository (GitHub)
View/report issues

Documentation

API reference

License

Icon for licenses.BSD-3-Clause (LICENSE)

Dependencies

flutter

More

Packages that depend on flutter_downloader