Flutter 下载器

用于创建和管理下载任务的插件。支持 iOS 和 Android。

该插件基于Android的[WorkManager][1]和iOS的[NSURLSessionDownloadTask][2]在后台模式下运行下载任务。

iOS集成

必需的配置

注意:以下步骤需要您在Xcode中打开ios项目。

  • 启用后台模式。
  • 添加sqlite库。

  • 配置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

或者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")!)
    }
}

可选配置

  • 支持HTTP请求:如果您想通过HTTP请求下载文件,您需要禁用Apple传输安全(ATS)功能。有两种选择:
  1. 仅为特定域禁用ATS:(将以下代码添加到您的Info.plist文件中)
<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. 完全禁用ATS:(将以下代码添加到您的Info.plist文件中)
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key><true/>
</dict>
  • 配置并发任务的最大数量:该插件默认允许同时运行3个下载任务(如果您加入的任务超过3个,只有3个任务会运行,其他任务会处于待定状态)。您可以通过将以下代码添加到您的Info.plist文件中来更改此数量。
<!-- changes this number to configure the maximum number of concurrent tasks -->
<key>FDMaximumConcurrentTasks</key>
<integer>5</integer>
  • 本地化通知消息:当您的应用程序不在前台运行时,该插件会发送通知消息以通知用户所有文件已下载。此消息默认为英语。您可以通过在Info.plist文件中添加和本地化以下消息来本地化此消息。(您可以在此[链接][3]中找到Info.plist本地化的详细信息)
<key>FDAllFilesDownloadedMessage</key>
<string>All files have been downloaded</string>

笔记

  • 该插件仅支持将文件保存在NSDocumentDirectory

Android集成

必需的配置

  • 如果您的项目运行在Flutter v1.12之前的版本,请参考本文档来配置您的Android项目。

  • 从Flutter v1.12开始,使用Android v2嵌入,在Android中与后台隔离工作无需额外配置(但您需要正确设置项目。请参阅升级pre-1.12的Android项目

  • 为了处理通知上的点击事件以在Android上打开已下载的文件,您需要添加一些额外的配置。将以下代码添加到您的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>

笔记

  • 您必须将下载的文件保存在外部存储中(其他应用程序可以读取您的文件的位置)
  • 只有当您的设备至少有一个可以读取这些文件类型(mp3、pdf等)的应用程序时,才能打开下载的文件。

可选配置

  • 配置并发任务的最大数量:该插件依赖于WorkManager库,而WorkManager取决于可用处理器的数量来配置同时运行的最大任务数。您可以通过将以下代码添加到您的AndroidManifest.xml来为此配置设置一个固定数量。
 <provider
     android:name="androidx.work.impl.WorkManagerInitializer"
     android:authorities="${applicationId}.workmanager-init"
     tools:node="remove" />

 <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>
  • 本地化通知消息:您可以通过本地化以下消息来本地化下载进度的通知消息。(您可以在此[链接][4]中找到Android中字符串本地化的详细信息)
<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:为了打开APK文件,您的应用程序需要REQUEST_INSTALL_PACKAGES权限。请将以下代码添加到您的AndroidManifest.xml
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

用法

导入包

import 'package:flutter_downloader/flutter_downloader.dart';

初始化

WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(
  debug: true // optional: set false to disable printing logs to console
);
  • 注意:在使用该插件之前必须对其进行初始化。

创建新的下载任务

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)
);

更新下载进度

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

重要提示:您的UI在主Isolate中渲染,而下载事件来自后台Isolate(换句话说,callback中的代码在后台Isolate中运行),因此您必须处理两个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 = data[1];
		int progress = data[2];
		setState((){ });
	});

	FlutterDownloader.registerCallback(downloadCallback);
}

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

static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
	final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port');
	send.send([id, status, progress]);
}

加载所有任务

final tasks = await FlutterDownloader.loadTasks();

带条件加载任务

final tasks = await FlutterDownloader.loadTasksWithRawQuery(query: query);
  • 注意:为了成功将数据解析为DownloadTask对象,您应该从数据库加载所有字段的数据(换句话说,使用:SELECT *)。例如:
SELECT * FROM task WHERE status=3
  • 注意:以下是此插件存储任务信息的task表的架构
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
);

取消任务

FlutterDownloader.cancel(taskId: taskId);

取消所有任务

FlutterDownloader.cancelAll();

暂停任务

FlutterDownloader.pause(taskId: taskId);

恢复任务

FlutterDownloader.resume(taskId: taskId);
  • 注意:resume()将返回一个新的taskId,对应于为继续下载过程而创建的新后台任务。您应该用这个新的taskId替换原始的taskId(其状态标记为paused),以继续跟踪下载进度。

重试失败的任务

FlutterDownloader.retry(taskId: taskId);
  • 注意:retry()将返回一个新的taskId(类似resume()

删除任务

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

打开和预览下载的文件

FlutterDownloader.open(taskId: taskId);
  • 注意:在Android上,只有当下载文件存储在外部存储中,并且您的设备上至少有一个可以读取该文件类型的应用程序时,您才能打开下载的文件。

GitHub

https://github.com/fluttercommunity/flutter_downloader