Saving Files to Application Folder and Gallery in Flutter

Paras Jain
6 min readDec 6, 2020

--

The majority of Mobile Applications require some kind of file storage, where users can download or save files for future access. Applications like WhatsApp & Instagram save files to their own Folder in Internal Phone Storage on Android and to the Photos Library on iOS Device.

In Flutter, to get information about storage Paths you can use the path_provider package, but by default, applications do not create their own application-specific folders in Internal Storage and that is what we will learn to do in this Article. So, let’s start this by creating a New Flutter Project and do some basic Project Setup:

Project Setup:

1.) Add Dependencies to “pubspec.yaml” file:

dio (3.0.10): To download file from a URL and save it to a given path.

path_provider (1.6.10): To get Application and System Specific Storage Paths.

image_gallery_saver (1.6.6): To save files to Photos Library on iOS.

permission_handler (5.0.1): To check/ask for storage permissions before performing any storage task.

2.) Once these dependencies are added, we need to do some changes to the android specific folder of our Flutter Project. For this let’s navigate to the AndroidManifest.xml file:

android>app>src>main>AndroidManifest.xml

In this file, add the following:

AndroidManifest.xml

android: requestLegacyExternalStorage=”true” is only required if you have a targetSDKVersion of 29 or above.

3.) Now, Let’s do the iOS specific changes. For this let’s navigate to the Info.plist file:

ios>Runner>Info.plist

In this file, add the following lines at bottom:

Info.plist

The permission we need to add is the NSPhotoLibraryUsageDescription to describe our reason for accessing the Photos Library.

UI Code

After the basic Project Setup, let’s create a basic UI for our Application. Our Application code in main.dart will look something like this:

The code above is a basic UI Design with a FlatButton. In MyHomePageState we have 2 state variables

  1. loading to indicate that the file is downloading
  2. progress to act as a progress value for LinearProgressIndicator

The onPressed property of FlatButton is provided with a function of downloadFile which will call the saveVideo function (yet to implement). The way this code is written is that the downloadFile function will handle the UI changes i.e setting the loading and progress variables and getting the boolean from saveVideo function to indicate to the user if the file is downloaded successfully or not.

Now that our UI code is done, let’s take a look at the implementation of saveVideo function.

“saveVideo” Function

The code for this function looks like this:

Now, let’s take a look at this in parts and understand what everything does.

On the first line in this function, we create an instance of Directory class which comes from dart:io package. We don’t give any value to this directly because this will get its value based on what platform our app runs on. For this have to write some platform-specific code next, and this code has the potential for throwing errors, and therefore we wrap everything in a try-catch block.

To check the platform we are on, we use Platform class , again from dart:io package. Now, in both cases of if-else we need to check/request permission. Clearly this code can be written in its own function, which for this tutorial we will call _requestPermission. The code for this function is as follows:

Permission class, the instance of which we pass to the _requestPermission function comes from the permission_handler package that we earlier added to the application.

In this function, we first asynchronously check, if the permission is granted. We return true if it is already granted, or else we request the permission. If the user grants this permission we return true, or else we return false.

On Android, we request for Permission.storage and on iOS we request for Permission.photos. Once these permissions are granted we do the following:

Android

We set the value of directory to getExternalStorageDirectory() which gives us a path like this:

/storage/emulated/0/Android/data/your_package_name/files

By default, each application has its own storage directory in “Android” folder of Device’s Internal Storage denoted by the package_name of that application.

But we do not want to save files to this directory. Instead what we want is to create our own application folder in the internal storage of our android device. One thing common in most android devices is that they have the “Android” folder in Device’s internal storage. So if we get a path leading to that “Android” directory, we can use the same path and create our application folder there. Lines 7–19 in the saveVideo function code are just for this.

iOS

On iOS Device we need to save files to Photos Library. We’ll get to this soon enough but before that, we will need to save the file in the Temporary Directory. For this, we set the directory to the result of getTemporaryDirectory().

Once the directories are set, we move out of the if-else block and we check if the directory exists? and if not, we try to create the directory. Now before moving to the next step of downloading the file, we check if the directory is successfully created or not. I am keeping this simple for this article and check the existence of the directory once again using directory.exists() which is an asynchronous function.

Now, if the directory exists we can move further and create a new File with the “filename” that we get as an argument to saveVideo function. Once this is done, we use the Dio package to download the file from a given URL and update progress using the onReceiveProgress property of dio.download function. Once the file is downloaded, on Android you can check the file in your Phone’s Internal Storage where you will find the Application Folder you specified.

Saving File to Photo Library on iOS

Once the Dio download function finishes execution, we can now save the download file to Photos Library on iOS device.

Earlier, we set the download directory in case of iOS device to Temporary Directory. This was because the package image_gallery_saver can copy the file from an existing directory to Photos Library. For this, we check if the Platform is iOS and then use saveFile function of ImageGallerySaver class and give it the path of the File we saved in Temporary Directory. One important thing to do here is to pass a “true” value to isReturnPathOfIOS parameter.

if (Platform.isIOS) {
await ImageGallerySaver.saveFile(saveFile.path,
isReturnPathOfIOS: true);
}

Voilà 💙

Now you can use this technique to do all sorts of custom storage behavior you want in your Flutter Apps, at least for Android.

Note: I’ve tried to keep the code for this tutorial fairly simple. For example, in the saveVideo function we just return a boolean value. In production apps, you might need to create different UI behaviour for different conditions. So take this tutorial as a source to learn the concept and implement this in your own ways.

If you like to have a visual representation for reference, check out this tutorial on my Youtube Channel and you’ll have a good idea! And while you’re there, if you find this useful, consider Subscribing and Sharing!

If you find this Useful, Consider Subscribing to RetroPortal Studio on Youtube and for future updates, Here are the links to my other Social Media Handles:

Twitter: https://www.youtube.com/theretroportal

Instagram: https://www.instagram.com/retroportalstudio

LinkedIn: https://www.linkedin.com/in/parasjainrps

Github: https://github.com/retroportalstudio

Happy Coding! ✌😁

--

--

Paras Jain

Mobile Application and Web Developer | @Youtube Content Creator | Worship #reactjs #flutter #java #dart | youtube.com/retroportalstudio