flutter_downloader
파일을 다운로드하는 것을 쉽게 만들어주는 강력한 플러그인입니다.
Flutter 다운로더 - 다운로드 작업을 생성하고 관리하기 위한 플러그인입니다.
{"sdk":"flutter"}{"sdk":"flutter"}^15.0.0아래는 영문 원문 스냅샷입니다. 최신 내용은 GitHub에서 확인하세요.
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.
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.
In previous versions of this package, there were known vulnerabilities related to SQL injection. SQL injection is a type of security vulnerability that can allow malicious users to manipulate SQL queries executed by an application, potentially leading to unauthorized access or manipulation of the database.
It is strongly recommended to upgrade to the latest version of this package to ensure that your application is not exposed to SQL injection vulnerabilities. The latest version contains the necessary security improvements and patches to mitigate such risks.
The following steps require to open your ios project in Xcode.
https://github.com/hnvn/flutter_downloader/blob/master/screenshot/enable_background_mode.png?raw=true
sqlite library.https://github.com/hnvn/flutter_downloader/blob/master/screenshot/add_sqlite_1.png?raw=true
https://github.com/hnvn/flutter_downloader/blob/master/screenshot/add_sqlite_2.png?raw=true
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")!)
}
}
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>
Info.plist file)<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>
Info.plist file.<!-- changes this number to configure the maximum number of concurrent tasks -->
<key>FDMaximumConcurrentTasks</key>
<integer>5</integer>
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:
NSDocumentDirectoryYou don't have to do anything extra to make the plugin work on Android.
There are although a few optional settings you might want to configure.
To make tapping on notification open the downloaded file on Android, add the
following code 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>
Notes
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 the
following code 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 -->
You can localize texts in download progress notifications by localizing following messages.
<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>
You can learn more about localization on Android here.
To open and install .apk files, your application needs
REQUEST_INSTALL_PACKAGES permission. Add the following in your
AndroidManifest.xml:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
See also:
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(/*...*/)
}
The directory must be created in advance.
After that, you need to provide the path of the directory in the savedDir parameter.
final taskId = await FlutterDownloader.enqueue(
url: 'your download link',
headers: {}, // optional: header send with url (auth token etc)
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)
);
await 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 = 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]);
}
@pragma('vm:entry-point') must be placed above the callback function to
avoid tree shaking in release mode for Android.
final List<DownloadTask>? tasks = await FlutterDownloader.loadTasks();
final List<DownloadTask>? 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
);
FlutterDownloader.cancel(taskId: taskId);
FlutterDownloader.cancelAll();
FlutterDownloader.pause(taskId: taskId);
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.
FlutterDownloader.retry(taskId: taskId);
retry() will return a new taskId (just like resume())
FlutterDownloader.remove(taskId: taskId, shouldDeleteContent:false);
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.
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!