flutter_downloader: background file downloads that survive a closed app
Flutter Downloader - A plugin for creating and managing download tasks.
At a glance
- What is it?
- flutter_downloader is a Flutter plugin that hands download work to WorkManager on Android and NSURLSessionDownloadTask on iOS, but the iOS side needs manual Xcode wiring and the file location strategy is still being redesigned.
- Who is it for?
- flutter_downloader is the right choice for a Flutter app that must fetch large files while it is closed and report progress when it comes back, provided you are prepared to do the iOS integration work in Xcode and to accept the plugin's current file location rules. It is the wrong dependency for a team that wants zero platform configuration, for a download target outside the iOS document directory, or for an app that must not trade transport security for plain HTTP.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 62 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
WorkManager on Android, NSURLSessionDownloadTask on iOS
Downloading a file inside a Flutter app looks easy until the app goes to the background, where the platform terminates processes that the operating system no longer needs. flutter_downloader exists for that case. The plugin is described as a plugin for creating and managing download tasks, it supports iOS and Android, and it delegates the actual work to the platform's own background download machinery: WorkManager on Android, NSURLSessionDownloadTask on iOS.
The reason to delegate rather than implement a downloader in Dart is visibility. A download driven by the platform scheduler survives the app being suspended or killed and reports back when the app is next in the foreground, which is exactly the behaviour a media app, an offline content reader or an update checker needs. The plugin also gives you a task queue with a concurrency limit, progress notifications and a callback channel for completion, all of which a plain Dart HTTP client would have to reinvent badly.
The audience is therefore Flutter developers building apps whose downloads must not stop when the user switches away, on both mobile platforms, with a plugin whose platform code is written in Kotlin on the Android side.
Getting the package is straightforward. The plugin is published on pub.dev as flutter_downloader, and the repository carries an example application, a test directory and a CHANGELOG.md, so the example project is the fastest way to see the API and the platform setup side by side.
Earlier versions carried SQL injection vulnerabilities
The README devotes a section to past versions and SQL injection vulnerabilities, and it is worth reading before anything else in the document. In previous versions of this package there were known vulnerabilities related to SQL injection, which the README describes as a type of security vulnerability that can let malicious users manipulate SQL queries executed by an application, potentially leading to unauthorised access or manipulation of the database.
The instruction is unambiguous: upgrade to the latest version to make sure your application is not exposed, because the latest version contains the security improvements and patches that mitigate the risk. This is a background download plugin that keeps a task database on the device, so the finding is not incidental to the feature, it is in the part of the plugin that stores the queue of pending work.
The release history gives that instruction a concrete target. Version 1.12.1 was published on 2026-08-22, version 1.12.0 on 2025-01-23 and version 1.11.8 on 2024-07-25, and the last push was on 2026-08-22. A project sitting on 1.11.x is carrying a known database flaw, so the version floor in your pubspec is a security decision rather than a formality. The licence field on the repository says BSD-3-Clause, which is permissive and carries an attribution requirement, so check that your notice file mentions the package if you ship it in a closed source application.
iOS needs three Xcode steps before a download can start
Android requires nothing extra to make the plugin work, according to the README. iOS is the other story, and the required configuration asks you to open the ios project in Xcode.
The three required steps are enabling background mode, adding the sqlite library, and configuring the AppDelegate. The AppDelegate step is the one that touches your code, because the plugin needs a registrant callback so that it can re-attach to the Flutter engine when a background download finishes while the app is not running. The Swift version of that wiring looks like this:
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)
}
}The Objective-C equivalent is given in the README as well, and the difference between the two is only the language syntax, not the shape of the registration. If your app already has an AppDelegate that other plugins registered through, you are merging this callback into existing code rather than adding it to an empty class.
The optional iOS configuration is where the environment variables of a mobile app become visible. To download over plain HTTP you must weaken Apple's transport security, either for one domain or globally, by adding an exception to Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>That disables a protection that exists for good reason, so it should be a deliberate decision for a specific legacy host, not a default.
Three concurrent tasks by default, and where the files can land
The plugin allows three download tasks running at a moment by default. If you enqueue more than three, only three run and the rest sit in a pending state. The limit is configurable in Info.plist, and the README shows the key and its type:
<!-- changes this number to configure the maximum number of concurrent tasks -->
<key>FDMaximumConcurrentTasks</key>
<integer>5</integer>On Android the README's own section on the maximum number of concurrent download tasks also notes that the plugin depends on WorkManager.
The file location rules are the harder constraint. The iOS section states that the plugin only supports saving files in NSDocumentDirectory, with no option given for other directories. On Android, the notes for opening a downloaded file from the notification say you have to save your downloaded files in external storage, where other applications have permission to read your files.
Two smaller details belong here too. On iOS the plugin sends a notification when all files are downloaded while the app is not in the foreground, and that message is English by default, so it is localised through a string key in Info.plist. And on Android, tapping the notification to open the file requires a content provider to be declared in 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>The downloaded files can only be opened if the device has at least one application able to read those file types, which is a small but real caveat for formats like mp3 or pdf on a locked down device.
The file location strategy is still being redesigned
The README carries a development note that says more about the project than any of the setup sections. The changes of external storage APIs in Android 11 cause some problems with the current implementation, and the author decided to redesign the plugin with a new strategy for managing the download file location. The redesign is described as still in triage and discussion in a pull request, and the author asks for contribution and feedback from Flutter developers to arrive at a better design for the plugin.
That is a candid statement, and it has direct consequences for anyone evaluating the package. The current approach to where files go is the part of the plugin that the author considers unsettled, and the Android 11 external storage change is the reason. An app that stores downloads in a location chosen for Android 10 may find that behaviour different on Android 11, and the direction of the redesign is not settled in the documentation.
The repository makes that note easy to find, since android_integration_note.md sits at the top level next to the android directory, and the same period also added a work manager configuration section to the Android integration guide. If your app's download storage model is the thing you care about most, read that file and the linked pull request before you commit to the dependency, because the plugin's answer to where a file lives is the part still under discussion.
The same note also tells you the plugin is in transition, and the release history backs that up, with 1.12.0 and then 1.12.1 arriving more than eighteen months apart.
Where this plugin is the wrong tool
Three cases stand out.
First, an app that only needs a small file over HTTPS with a foreground request has no reason to involve background scheduling. The plugin's value is entirely in the work surviving process death, and the cost is iOS platform configuration, an AppDelegate edit, a content provider on Android and a storage model with real constraints.
Second, an app that cannot accept the storage rules. iOS files must land in NSDocumentDirectory, and Android files that the user is expected to open must be in external storage. A product that needs a different directory, for example one that manages its own cache layout or wants files in a location the platform sandboxes differently, has to work around the plugin rather than with it.
Third, plain HTTP. Supporting an insecure transfer on iOS means weakening App Transport Security in Info.plist, and the README is candid that this is how the feature is enabled. On a device that manages the app's network security posture, that is often a review item on its own.
The realistic alternative is to keep the download inside the app and accept that it stops when the app is backgrounded, which the platform HTTP client already does for you. The difference in approach is exactly the thing the plugin trades away: with a background downloader the file arrives even if the user closes the app, and you inherit the queue, the notification behaviour and the platform setup. Between the two, a small utility that fetches an image in the foreground should not take on the plugin's configuration burden, while a media catalogue or offline reader should.
Editorial conclusion
flutter_downloader is the right choice for a Flutter app that must fetch large files while it is closed and report progress when it comes back, provided you are prepared to do the iOS integration work in Xcode and to accept the plugin's current file location rules. It is the wrong dependency for a team that wants zero platform configuration, for a download target outside the iOS document directory, or for an app that must not trade transport security for plain HTTP. Before you adopt it, move off any release older than 1.12 because of the SQL injection history, read the triage discussion in pull request 550 about the redesigned file location strategy, and confirm the concurrency cap of FDMaximumConcurrentTasks suits the device you target.
Frequently asked questions
What does the flutter_downloader plugin do on Android and iOS?
It creates and manages download tasks that run in the background, using WorkManager on Android and NSURLSessionDownloadTask on iOS. The plugin notifies you when downloads finish, including when the app was not in the foreground.
Is it safe to use an older version of flutter_downloader?
The README states that previous versions of the package had known SQL injection vulnerabilities and strongly recommends upgrading to the latest version, which contains the patches. Version 1.12.1 is the most recent release, published on 2026-08-22.
How many downloads can flutter_downloader run at the same time?
The plugin allows three download tasks running at a moment by default, and any further tasks are put in a pending state. On iOS the limit is changed with the FDMaximumConcurrentTasks key in Info.plist.
Where can downloaded files be saved on iOS?
The plugin only supports saving files in NSDocumentDirectory. On Android, the notes for opening a downloaded file say you have to save files in external storage, where other applications have permission to read them.
What setup does flutter_downloader need on iOS?
Three required steps in Xcode: enable background mode, add the sqlite library, and configure the AppDelegate so the plugin can register itself and receive background download callbacks. An Objective-C and a Swift version of the AppDelegate wiring are both given in the README.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fluttercommunity-flutter-downloader)Was this useful?