flutter_downloader

repository·master·Indexed 21 days ago

https://github.com/fluttercommunity/flutter_downloader

A Flutter plugin for creating and managing download tasks with background support on iOS and Android. It utilizes WorkManager on Android and NSURLSessionDownloadTask on iOS. The library provides functionality to enqueue, pause, resume, cancel, and retry downloads, as well as query task status via a database schema. It includes support for background isolates, custom notification localization, and configuration for concurrent task limits.

Tokens
7.5K
Snippets
21
Records
24
Agent score
76%

What's inside flutter_downloader

  1. Handle download progress via background isolates

    master

    Download events are emitted from a background isolate, while your UI runs on the main isolate. To update your UI, you must use an IsolateNameServer to communicate between them.

    Steps to implement:

    1. Create a ReceivePort in your UI class.
    2. Register the SendPort with a unique name using IsolateNameServer.registerPortWithName.
    3. Listen to the port to receive updates.
    4. Define a top-level or static callback function decorated with @pragma('vm:entry-point') to prevent tree shaking in release mode.
    5. In the callback, look up the SendPort by name and send the data back to the main isolate.
    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 = DownloadTaskStatus.fromInt(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, int status, int progress) {
      final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port');
      send?.send([id, status, progress]);
    }
  2. Customize iOS launch screen assets

    master

    To change the appearance of the launch screen on iOS, you can replace the existing image files within the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory with your own assets.

    Alternatively, you can manage these assets using Xcode:

    1. Open the iOS project in Xcode using open ios/Runner.xcworkspace.
    2. In the Project Navigator, navigate to Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog.
    open ios/Runner.xcworkspace
  3. Enable opening downloaded files from notifications on Android

    master

    To allow users to click a notification and open the downloaded file, you must configure a FileProvider in your AndroidManifest.xml.

    Requirements:

    • Downloaded files must be saved in external storage so other applications have permission to read them.
    • The device must have an application capable of reading the specific file type (e.g., an MP3 player for .mp3 files).
    <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>
  4. Configure the Android Application class for background execution

    master

    To enable flutter_downloader to work in a background isolate, you must implement a custom Application class that implements PluginRegistry.PluginRegistrantCallback. This ensures the plugin is registered with the special FlutterEngine used for background execution.

    1. Create a MyApplication class in your Android project (Java or Kotlin).
    2. In the registerWith method, ensure GeneratedPluginRegistrant.registerWith(registry) is called.
    3. Important: If you are using other plugins that require UI manipulation alongside flutter_downloader, you should explicitly register FlutterDownloaderPlugin to avoid conflicts.
    4. Update your AndroidManifest.xml to use your new MyApplication class via the android:name attribute.
    // MyApplication.kt
    import io.flutter.app.FlutterApplication
    import io.flutter.plugin.common.PluginRegistry
    import io.flutter.plugins.GeneratedPluginRegistrant
    import vn.hunghd.flutterdownloader.FlutterDownloaderPlugin
    
    internal class MyApplication : FlutterApplication(), PluginRegistry.PluginRegistrantCallback {
        override fun registerWith(registry: PluginRegistry) {
            if (!registry.hasPlugin("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin")) {
                FlutterDownloaderPlugin.registerWith(registry.registrarFor("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin"))
            }
            GeneratedPluginRegistrant.registerWith(registry)
        }
    }
    <!-- AndroidManifest.xml -->
    <application
            android:name=".MyApplication"
            ....>
  5. Initialize FlutterDownloader

    master

    Before using the plugin, you must initialize it in your main() function. This ensures the background services are ready.

    Key options:

    • debug: (bool, default: true) Set to false to disable console logs.
    • ignoreSsl: (bool, default: false) Set to true to allow working with http links instead of strictly https.
    import 'package:flutter_downloader/flutter_downloader.dart';
    
    void main() {
      WidgetsFlutterBinding.ensureInitialized();
    
      await FlutterDownloader.initialize(
        debug: true,
        ignoreSsl: true
      );
    
      runApp(/*...*/);
    }
  6. Configure Android integration for Flutter Downloader

    master

    While basic functionality works on Android without extra steps, several optional configurations are available via AndroidManifest.xml.

    Open downloaded files from notifications

    To allow users to tap a notification and open the downloaded file, add a DownloadedFileProvider to your AndroidManifest.xml. Note: Files must be saved in external storage to be accessible by other applications.

    Configure maximum concurrent tasks

    The plugin uses WorkManager, which defaults to the number of available processors. To set a fixed number of concurrent tasks, you must disable the default WorkManager initializer and declare a customized FlutterDownloaderInitializer with the MAX_CONCURRENT_TASKS metadata.

    Install .apk files

    To allow your application to open and install .apk files, you must request the REQUEST_INSTALL_PACKAGES permission.

    <!-- Open downloaded file from notification -->
    <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>
    
    <!-- Install .apk files -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
  7. Configure iOS integration for Flutter Downloader

    master

    To use flutter_downloader on iOS, you must perform several required configuration steps in Xcode.

    Required Steps

    1. Enable Background Mode: In your Xcode project settings, enable the background mode capability.
    2. Add sqlite library: Add the sqlite library to your iOS project via Xcode.
    3. Configure AppDelegate: You must register a plugin registrant callback to allow background execution.

    Important Limitations

    • This plugin only supports saving files in the NSDocumentDirectory on iOS.
    // Swift AppDelegate configuration
    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")!)
        }
    }
  8. Configure maximum concurrent tasks on Android

    master

    To override the default WorkManager task count on Android, you must disable the default InitializationProvider and provide a custom FlutterDownloaderInitializer in your AndroidManifest.xml.

    Use the MAX_CONCURRENT_TASKS metadata key within the custom initializer to set your desired number.

    <!-- 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 -->
  9. Configure maximum concurrent download tasks

    master

    By default, the plugin uses WorkManager, which scales tasks based on available processors. To set a fixed limit for the maximum number of concurrent download tasks, add the following configuration to your AndroidManifest.xml. You must first disable the default WorkManagerInitializer to use the custom FlutterDownloaderInitializer.

     <provider
         android:name="androidx.work.impl.WorkManagerInitializer"
         android:authorities="${applicationId}.workmanager-init"
         android:enabled="false"
         android:exported="false" />
    
     <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>
  10. How to track download progress with a callback

    master

    Because downloads run in a background isolate, you must use a ReceivePort and IsolateNameServer to communicate progress updates back to your main UI isolate.

    Steps:

    1. Create a ReceivePort in your UI.
    2. Register the port with IsolateNameServer using a unique name.
    3. Listen to the port to receive updates.
    4. Define a top-level or static function as your DownloadCallback.
    5. Inside the callback, look up the SendPort via the IsolateNameServer and send the data back to the UI.
    6. Register the callback using FlutterDownloader.registerCallback(callback).
    ReceivePort _port = ReceivePort();
    
    @override
    void initState() {
      super.initState();
      // 1. Register the port
      IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
      
      // 2. Listen for data
      _port.listen((dynamic data) {
         String id = data[0];
         DownloadTaskStatus status = DownloadTaskStatus(data[1]);
         int progress = data[2];
         setState(() { /* Update UI */ });
      });
    
      // 3. Register the callback
      FlutterDownloader.registerCallback(downloadCallback);
    }
    
    // 4. The callback MUST be top-level or static
    static void downloadCallback(String id, int status, int progress) {
      final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port');
      send?.send([id, status, progress]);
    }
  11. Configure iOS optional settings via Info.plist

    master

    You can customize several behaviors for iOS using keys in your Info.plist file.

    Support HTTP requests (Disable ATS)

    By default, iOS restricts insecure HTTP connections. To allow downloads via HTTP, you can either:

    • Disable ATS for a specific domain: Use NSExceptionDomains to whitelist a specific server.
    • Completely disable ATS: Use NSAllowsArbitraryLoads to allow all insecure connections.

    Configure maximum concurrent tasks

    By default, the plugin allows 3 concurrent download tasks. To change this, set the FDMaximumConcurrentTasks key to an integer.

    Localize notification messages

    To change the English default message sent when all files are downloaded while the app is in the background, localize the FDAllFilesDownloadedMessage key.

    <!-- Configure maximum number of concurrent tasks -->
    <key>FDMaximumConcurrentTasks</key>
    <integer>5</integer>
    
    <!-- Localize notification message -->
    <key>FDAllFilesDownloadedMessage</key>
    <string>All files have been downloaded</string>
    
    <!-- Completely disable ATS -->
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key><true/>
    </dict>