flutter_downloader icon indicating copy to clipboard operation
flutter_downloader copied to clipboard

`background_downloader` – alternative to this plugin

Open 781flyingdutchman opened this issue 2 years ago • 22 comments

I've used flutter_downloader and contributed a few improvements, but have found the plugin as a whole has too many issues that make it difficult to use. I've written an alternative, background_downloader, and would like this community's feedback on:

  1. The background_downloader plugin functionality
  2. Whether we could consider the background_downloader as V2.0 of the flutter_downloader. I have no strong preference for this, as I am happily using it myself, but I see lots of issues posted for flutter_downloader, and because it is the top result when searching on Pub Dev for a downloader, I could see others benefiting from a reliable V2.0.

Here's what I believe background_downloader has over flutter_downloader:

  1. Supports more platforms (iOS, Android, MacOS, WIndows, Linux) fixing #391 #617 #651
  2. Is consistent in behavior across all platforms, so no need at all for platform-specific code like if (Platform.isAndoid)
  3. Is thoroughly tested: currently ~~53~~ 71 integration tests pass on all platforms (versus flutter_downloader currently looking at 275 open issues, of which 46 are bugs, many due to the complexity and inconsistency of the code and API)
  4. Offer downloads, uploads and HTTP requests using a simple API, fixing #410
  5. Offers three ways to monitor a task: 1) anonymous callback when you make a download or upload call, 2) listen to a single updates stream where all tasks can be monitored centrally, or 3) register callbacks for all tasks or by group of tasks. In all cases, you can choose to monitor status (e.g. failed) and/or progress (% of file downloaded)
  6. Offers batch downloads and uploads with batch progress monitoring
  7. Has a built-in retries (if you want those), waiting for WiFi availability, headers, url query parameters, support for POST requests, and metadata
  8. Has solid queue management (query queue to get list of running task, cancelling tasks, etc), without needing a SQL database in each platform
  9. Has a clean and consistent API: no need for initialization, no @pragma-vm or isolate voodoo
  10. Is fast and reliable. I use it to download >1000 files in one queue, and also to download multiple 50Mb files in parallel in the background and have no issues. Feedback from other users so far has been positive.
  11. Is well documented

Here's what it doesn't do that flutter_downloader does, and why I chose not to:

  1. ~~Android notification while downloading. This is Android-specific functionality (violates benefit 2 above), adds tremendous complexity (see all the issues!) and it's trivial to implement a progress indicator in Flutter that shows when your app is in the foreground (see the example app)~~ Notifications for Android and iOS are now supported in version 5.3.0, and work with Android 13
  2. ~~Storing in Android external Files directory. Again this is Android specific (violates benefit 2 above) and is trivial to implement if you really need it by simply moving the file to that destination upon completion of the download~~ Scoped storage on Android implemented in version 5.4.0 and platform-dependent on other platforms
  3. ~~Opening a file from the notification. I consider that not a download functionality but a 'what do you do with a downloaded file' functionality, and it therefore does not belong in a downloader. If you need this, I think we could develop a separate package that does that (it may already exist)~~ Implemented in V5.6.0
  4. ~~Database. I believe one should monitor tasks via callbacks, and use queue management (which is available) in exceptional cases (e.g. upon start-up). Losing a complex SQL database (implemented on every platform) that is the cause of many issues in flutter_downloader is an advantage~~ Database is now supported in version 4.1.0
  5. ~~Pause a task, or do partial download and resume. These are edge cases, add complexity and have disadvantages, so I don't think they are worth implementing~~ Pause/resume is now supported in version 5.0.0

I may have missed a few differences. Point is that if this were to become V2.0 there would be a significant improvement in functionality (IMHO), but also some regression that I don't believe we should fix (e.g. we should not try to implement notifications, external files directory etc. or we'll end up with a rather messy API and inconsistent implementations across platforms again). Some users may be disappointed (though they could continue to use the V1 branch of flutter_downloader).

Again, not trying to push this onto the community, just looking for feedback and offering this as an option. Looking forward to your thoughts.

@bartekpacia thanks for taking over as the maintainer of flutter_downloader: I know it's a lot of work!

781flyingdutchman avatar Feb 24 '23 05:02 781flyingdutchman

For the PBS radio app (Melbourne) use case, it's been quite useful having the database layer for two reasons.

First being we can easily list all the downloads without having to maintain our own database of downloads. Secondly we can easily lookup current download state's for an episode at any point in time, this means if destroying and recreating a widget we can perform a lookup of an episode's download state and display current download progress.

Granted we can create this database layer ourselves ontop of proposed changes. Just my two cents!

markst avatar Feb 24 '23 08:02 markst

Having said that, it's great to see some momentum on flutter_downloader and nice lot of input on the PR!

markst avatar Feb 24 '23 08:02 markst

Thanks for the feedback. I caved on persistent storage :) and added optional tracking, which stores the TaskStatus and progress for each tracked task in persistent storage (now in version 4.1.0)

Because there is some performance penalty for tracking, and not everyone needs it, here's how it works:

  • When starting or resuming the app, call FileDownloader().trackTasks() to turn on tracking. You can specify a group parameter to only track tasks in that group, and you can call this multiple times for every group you want to track.
  • Tasks are tracked, and you can get the data by calling FileDownloader().database.recordForId(taskId), which returns a TaskRecord which has fields for task, status and progress. Of course you can also get allRecords (by group) and do things like deleteRecordWithId, all via FileDownloader().database.

As a bonus, in case somehow the completion of a background download was missed when your app was suspended, when you activate tracking by calling trackTasks, the downloader iterates over all tasks in the database that are not marked as completed, and checks if a file exists in the target download location. If it does exist, we assume the task was downloaded successfully (only successful tasks are copied from a temp location to their destination, so this is usually reasonable), update the record in the database and send updates to the task listener/callback. If you don't want this check, you can set markDownloadedComplete to false in the call to trackTasks.

@markst would love your feedback on this feature.

781flyingdutchman avatar Feb 25 '23 09:02 781flyingdutchman

Your plugin looks great @781flyingdutchman – thanks for creating it.

There's one more advantage that , I'd say, flutter_downloader has over background_downloader – and it's flutter_downloader being in fluttercommunity organization. But I see no problems in getting background_downloader in here as well :)

I think that in the future, it'd be great to have a single, recommended way of doing background downloads in Flutter, with all the platform-specific features available as well (kind of like flutter_local_notifications does).

bartekpacia avatar Feb 25 '23 20:02 bartekpacia

Exactly, that's why I'm offering/suggesting to simply make V2.0 of flutter_downloader what is now background_downloader (and retire background_downloader). The only reason I have it as a separate package is because the flutter_downloader simply doesn't work reliably enough for my purpose - I started with a fork and then decided I might as well rewrite it, then started adding stuff I personally don't really need!

I'm also happy to put background_downloader in the fluttercommunity, though I have no idea what that means and feel like we'd still be maintaining two downloader packages.

781flyingdutchman avatar Feb 25 '23 22:02 781flyingdutchman

Nice plugin , It looks like it can return received bytes on downloading, which flutter_downloader cannot do at this point

kennir avatar Feb 26 '23 05:02 kennir

Thanks for the feedback. I caved on persistent storage :) and added optional tracking, which stores the TaskStatus and progress for each tracked task in persistent storage (now in version 4.1.0)

@781flyingdutchman hate to be responsible for you caving!

However it does mean our use case is resolved:

deeep-space-downloads mp4

markst avatar Feb 27 '23 05:02 markst

Awesome!

781flyingdutchman avatar Feb 27 '23 08:02 781flyingdutchman

V5.0.0 now supports pause/resume as well

781flyingdutchman avatar Mar 05 '23 06:03 781flyingdutchman

V5.1.0 addresses the download time limit for Android. You can now download for as long as you want.

781flyingdutchman avatar Mar 06 '23 00:03 781flyingdutchman

V5.3.0 adds notifications on Android and iOS

781flyingdutchman avatar Mar 25 '23 06:03 781flyingdutchman

V5.4.0 adds scoped storage on Android and - to a degree- shared storage on other platforms (subject to platform constraints).

781flyingdutchman avatar Mar 28 '23 03:03 781flyingdutchman

@781flyingdutchman looks promising. Do you know if background_downloader is free from the new bug in flutter_downloader where it doesn't show the download progress? https://github.com/fluttercommunity/flutter_downloader/issues/844

riverscuomo avatar Apr 08 '23 14:04 riverscuomo

background_downloader is unrelated to flutter_downloader so I don't expect bugs that apply to one to apply to the other. Give it a try!

781flyingdutchman avatar Apr 08 '23 15:04 781flyingdutchman

Thanks. We were able to fix our problem with the solution here: fluttercommunity/flutter_downloader#807 But we will keep this alternative package in mind for the future.

riverscuomo avatar Apr 08 '23 17:04 riverscuomo

Version 5.6.0 adds option to open a file directly from the notification, or programatically

781flyingdutchman avatar Apr 30 '23 06:04 781flyingdutchman

can you provide me code how it's working currently i am using flutter downloader and it's tough to show the progress indicator i am trying to switch to background_dwonloader i am haing issue with loading button can you share me repo where you have used @781flyingdutchman @riverscuomo @markst it would be so grateful .

Sushil787 avatar May 17 '23 16:05 Sushil787

Hi, here's the repo. It has an example app that shows how to show a progress indicator - there are many and simpler ways to do it. You can just listen to the 'updates' field of the downloader and if the update is a 'TaskProgressUpdate' and the 'progress' is positive, you pass it to your progress bar widget. Just make sure to set the 'updates' field of your 'DownloadTask' such that you receive progress updates (by default you don't).

781flyingdutchman avatar May 17 '23 17:05 781flyingdutchman

The latest version of background_downloader includes a DownloadProgressIndicator that is configurable and easily hooked up to the downloader, as illustrated in the example app in the repo.

781flyingdutchman avatar Jun 15 '23 02:06 781flyingdutchman

However it does mean our use case is resolved:

deeep-space-downloads mp4

@markst Awesome example! I'm trying to do a similar use case. Would you mind to share more details of your approach?

beroso avatar Jul 21 '23 22:07 beroso

@781flyingdutchman

On lieu of the Google Play policy update requiring us to target Android API level 33, I tried to migrate from flutter_downloader to background_downloader, and I think there are still very limited examples to the alternate package to get started on it right away.

The example that comes with the package should provide some comprehensive guide to basic download functionalities. Like, downloading and saving to a shared storage (even though it is present in the docs) as mentioned in the doc is unstable. It sometimes works, sometimes doesn't. (Checked this for Android Pixel 6 API 33). The example uses records from Dart, which requires most of us to bump Dart versions in pubspec.yaml manually for a lower limit of >3.0.0. (This is fine, but a little unusual as most packages do not have such strict dependencies for examples). If we have two screens in the app, and the example screen is loaded the second time, there's a bunch of errors in the screen. (Maybe you'll have to do something with disposing of streams or maintaining the state in a different way)

background_downloader sounds promising and I'm pointing out the things I am facing currently trying to implement it in a production app, so we can smoothen the dev exp.

praveengitsit avatar Sep 06 '23 14:09 praveengitsit

Hi @praveengitsit , thanks for checking it out. If you encounter issues with the move to shared storage, please file that as a bug. As for documentation and example, I am happy to consider a pull request with improvements. As it stands, I'd say the documentation is far more extensive and exhaustive than most packages provide, and the example does the basic job of showing how to use it - I don't think there's a reasonable expectation to make it work with multi-screen apps, but again I am open to looking at suggested changes to make it a better experience!

781flyingdutchman avatar Sep 07 '23 06:09 781flyingdutchman