How To Write a Simple Downloading App With Flutter

Michał Lot
5 min readApr 27, 2020

--

illustrations | unDraw

Intro

Edit 2024: Keep in mind that this story was written about 4 years ago in 2020 and it is or could be already deprecated.✌️

In this article you will learn how to write a simple app which:

  • downloads a file
  • shows download progress
  • stores downloaded file available outside the app
  • shows local notification with a download result
  • opens downloaded file in a native app by tapping the notification

If you are new to Flutter and you have never built nor created an application using Flutter, please get familiar with Get started section on the official flutter.dev website before continuing.

Click here to find out how to set up your development environment.

End result

file_download_demo.gif

Step 0. Setting up the project

First of all, create a new Flutter project if you haven’t done it already :-)

Step 1. Adding dependencies

Let’s start with a few packages that are going to help us achieving our final result. Add following dependencies to the pubspec.yaml file:

dio: ^3.0.9
path_provider: ^1.6.5
downloads_path_provider_28: ^0.1.0
permission_handler: ^4.4.0+hotfix.2
open_file: ^3.0.1
flutter_local_notifications: ^1.3.0

dio — a powerful http client for Dart, which supports file downloading among other things,

path_provider — plugin for finding commonly used locations in the filesystem, in our case used for a particular application document directory on iOS,

downloads_path_provider_28 — plugin to get the downloads directory on Android,

permission_handler — plugin to check and request permissions,

open_file —plugin which can call a native app to open file with certain extension,

flutter_local_notifications — plugin for displaying local notifications.

Step 2. Info.plist and AndroidManifest.xml

  • iOS

To allow arbitrary loads and to be able to see downloaded files in app directory (Locations/On My iPhone/<app_name>) you have to extend your info.plist with keys listed below.

<key>NSAllowsArbitraryLoads</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>

Detailed info about used keys:

  • NSAllowsArbitraryLoads — click
  • LSSupportsOpeningDocumentsInPlace — click
  • UIFileSharingEnabled — click

Setup flutter_local_notifications plugin as described on the iOS integration section of the plugin readme. For our needs, we just need to copy one if statement, depending on what iOS language in our project we use.

Add the following lines to the didFinishLaunchingWithOptions method in the AppDelegate.m/AppDelegate.swift file of your iOS project

Swift:

Objective-C:

  • Android

All we have to do in our Android project, is to add Internet and read/write external storage permissions to AndroidManifest.xml file.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Detailed info about used permissions:

  • android.permission.INTERNET — click
  • android.permission.READ_EXTERNAL_STORAGE — click
  • android.permission.WRITE_EXTERNAL_STORAGE — click

Step 3. Starting point

Copy and replace _MyHomePageState class as our starting point and let’s start coding! As you can see, all I have changed in the code below are just the small things like icons and labels.

Step 4. Resources

To download anything, we need to have url to the file we are going to get, so let’s specify it. Add two final Strings with url and filename. Just to make it simple we are going to hardcode the resources. In a real life scenario you would probably get the resource URL from your database or Web API.

Step 5. Downloads directory path

We already know the filename and url to the file we want to download, so now it’s time to get the directory where we will store it. Depending on the platform we are going to get different paths since both operating systems have different policies of storing files.

If you are interested in this topic, read more about it here for iOS and here for Android.

The simplest way to get our download path is to use path_provider and downloads_path_provider_28 just like this:

Step 6. Storage permissions

Check and request for the storage permissions. Even though we have added appropriate keys to our native configuration files we still need to make sure if we are allowed to access the storage.

Step 7. Download method

We already know what we want to download, where to store it and if we have sufficient permissions to do this. Actually, the next step is to download… But right before this, let’s fill body of our _download() method with the things we know.

To combine our directory path and filename we need to import dart path package.

Step 8. Download process

Finally, it’s time to download our file. Create an instance of Dio and add _startDownload() method which will download a file from the provided url to “save path”.

To update download progress, we need to add one more method in which we will calculate the percentage download status.

Simple as that.

Now, to enable the process of calculating the progress, we have to pass our method as a parameter to Dio’s download function.

Build and run the example and you should already be able to download the file and see the progress updated as shown below.

progress.png

Step 9. Local notifications initialization

Create a new instance of the flutter_local_notifications plugin and initialize it.

Initialisation should only be done once and the place where this can be done is in the main function, or alternatively within the first shown page of your app. Override initState and initialize it.

For now, add also an empty onSelectNotification method.

Step 10. Calling _showNotification method on downloads finish

After the download is completed, we need to show the notification whether it has succeeded or not. Modify our _startDownload method.

We have added simple Map with three values to determine if a download is completed successfully and to store a file path or error message. The logic is also closed within try catch finally clause which finally calls _showNotification.

Step 11. Showing local notification

Add _showNotification method. In this method we will call show function to show simple notification.

Except the basic configuration needed to show the notification I have also encoded our Map to JSON and passed it to a payload parameter. The payload String will be used in the method fired when we tap the notification.

Step 12. Handling notification tap

The last thing we have to do is to fulfill our empty _onSelectNotification method. Here we will just decode our encoded Map to JSON String and open downloaded file if the download succeeded or show dialog with an error message.

That’s it!

The Great Gatsby (2013) — The Mysterious Mr. Gatsby Scene

You can find the full example in my GitHub repository.

https://buymeacoffee.com/lorien.dev

--

--

Michał Lot

Freelance Software Developer. Photographer and running freak. 💻📷🏃‍♂️✌️