17

In my flutter application I can create a pdf file, then I want to save it in Download folder. I'm trying to achieve this goal with path_provider package, but I can't.

This is the sample code from flutter's cookbook, If I use it I don't get any error, but I don't find the file either.

final directory = await getApplicationDocumentsDirectory();
File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');

What's the correct way to do it?

2
  • What if this case is for flutter web ? Commented May 20, 2022 at 6:24
  • 1
    Never used before, but file_saver package seems to be a good solution Commented May 20, 2022 at 11:24

5 Answers 5

12

To find the correct path please use ext_storage. You will need this permission

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

on Android 10 you need this in your manifest

<application
      android:requestLegacyExternalStorage="true"

on Android 11 use this instead

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

Remember to ask for them using permission_handler

I leave you my code:

  static Future saveInStorage(
      String fileName, File file, String extension) async {
    await _checkPermission();
    String _localPath = (await ExtStorage.getExternalStoragePublicDirectory(
        ExtStorage.DIRECTORY_DOWNLOADS))!;
    String filePath =
        _localPath + "/" + fileName.trim() + "_" + Uuid().v4() + extension;

    File fileDef = File(filePath);
    await fileDef.create(recursive: true);
    Uint8List bytes = await file.readAsBytes();
    await fileDef.writeAsBytes(bytes);
  }
Sign up to request clarification or add additional context in comments.

6 Comments

ext_storage is discontinued - any good alternatives available?
any alternatives?
Hi, is this work on both android and iOS?
path_provider seems like a good alternative: pub.dev/packages/path_provider
@AndrePerkins Unfortunately Download directory is not supported at the moment
|
10

Android 11 changed a lot of things with its emphasis on scoped storage. Although /storage/emulated/0/Android/data/com.my.app/files is one of the directory paths given by the path_provider pkg, you won't be able to see files saved in /storage/emulated/0/Android/data/com.my.app/files just using any run-of-the-mill file application (Google Files, Samsung My Files, etc.).

A way to get around this (although it only works on Android) is to specify the "general" downloads folder as shown below.

Directory generalDownloadDir = Directory('/storage/emulated/0/Download');

if you write whatever file you are trying to save to that directory, it will show up in the Downloads folder in any standard file manager application, rather than just the application-specific directory that the path_provider pkg provides.

Below is some test code from an app I'm building where I save a user-generated QR code to the user's device. Just for more clarity.

//this code "wraps" the qr widget into an image format
                          RenderRepaintBoundary boundary = key.currentContext!
                              .findRenderObject() as RenderRepaintBoundary;
                          //captures qr image 
                          var image = await boundary.toImage();

                          String qrName = qrTextController.text;
                          
                          ByteData? byteData =
                              await image.toByteData(format: ImageByteFormat.png);
                          Uint8List pngBytes = byteData!.buffer.asUint8List();

                          //general downloads folder (accessible by files app) ANDROID ONLY
                          Directory generalDownloadDir = Directory('/storage/emulated/0/Download'); //! THIS WORKS for android only !!!!!! 

                          //qr image file saved to general downloads folder
                          File qrJpg = await File('${generalDownloadDir.path}/$qrName.jpg').create();    
                          await qrJpg.writeAsBytes(pngBytes);

                          Fluttertoast.showToast(msg: ' $qrName QR code was downloaded to ' + generalDownloadDir.path.toString(), gravity: ToastGravity.TOP);

2 Comments

does it needs storage permission?
I have a problem, when I reinstall my app it says that this directory already exists, but why with getApplicationDocumentsDirectory sais that it doesn't exist ?
1

You want getExternalStorageDirectories. You can pass a parameter to specify the downloads specifically:

final directory = (await getExternalStorageDirectories(type: StorageDirectory.downloads)).first!;

File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');

If you're using null safety you don't need the bang operator:

final directory = (await getExternalStorageDirectories(type: StorageDirectory.downloads)).first;

File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');

7 Comments

I've tried to print the path and I think that's correct: "/storage/emulated/0/Android/data/com.my.app/files/Download" but I can't find the file, any ideas?
@EmanueleVinci You probably have an exception you ignore somewhere. You likely didn't give the permission to write to external storage. See this so answer for how: stackoverflow.com/a/50571167/13250142. You need both a permission in the manifest and at runtime. This is a guess though so please provide more detail as to what you've done to find the file, output from the code you're running, anything you've tried to solve it(e.g. immediatly reading back the file you just wrong in your dart code).
Half-solved the problem! I have <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>, I ask correctly the permission, but the problem is that with Android 11 you need <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />. Anyway, the path "/storage/emulated/0/Android/data/com.my.app/files/Download" is not accessible from the Files application. Any ideas?
@EmanueleVinci What is the exact error.
You cant use getExternalStorageDirectories as that saves it to the app directory. as Emanuele commented with his path print statement. "/storage/emulated/0/Android/data/com.my.app/files/Download". Anything in that com.my.app folder wont be accessible from the users stand point like file explorer. I'm still trying to figure out how to save to the downloads folder myself and am not finding any solutions.
|
0

I just want to give some up-to-date information on the topic, because I searched the web and indeed after Android 11, things are changed (I also remember having troubles when writing native for Android).

  • Flutter 3.32.5

  • Dart 3.8.1

  • Emulator Android 16 (API level 36.1)

The following methods from the package `path_provider` give you:

var directory = await getDownloadsDirectory()
// -> /storage/emulated/0/Android/data/com.example.my_app/files/downloads
var directory = await getExternalStorageDirectory();
// -> /storage/emulated/0/Android/data/com.example.my_app/files
var directory = await getApplicationDocumentsDirectory();
// -> /data/user/0/com.example.my_app/app_flutter

So all these locations are on the internal storage and are application specific.

The only way to get the "real" Download folders is like the post above suggests:

Directory generalDownloadDir = Directory('/storage/emulated/0/Download');

This is the only workaround so far. Even in native I remember I was not able to find a proper API method or something so you have to go with custom path. Good thing is that (like in my case) you can chose another folder e.g. `/storage/emulated/0/Pictures/MyApp` and it will work fine as wlong as you have the required permission (MANAGE_EXTERNAL_STORAGE) of course.

PS. I have not tested the code in iOS.

Comments

-1

For downloading file in Downloads folder, here are examples:

// Save multiple files
DocumentFileSave.saveMultipleFiles([textBytes, textBytes2], ["text1.txt", "text2.txt"], ["text/plain", "text/plain"]);

//Save single text file
DocumentFileSave.saveFile(textBytes, "my_sample_file.txt", "text/plain");

//Save single pdf file
DocumentFileSave.saveFile(pdfBytes, "my_sample_file.pdf", "appliation/pdf");

//Save single image file
DocumentFileSave.saveFile(imageJPGBytes, "my_sample_file.jpg", "image/jpeg");

More details of library here: https://pub.dev/packages/document_file_save_plus

1 Comment

This package is not updated to reflect the Android 11 changes with the new permission MANAGE_EXTERNAL_STORAGE.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.