'How to save File in Downloads folder in flutter?

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?



Solution 1:[1]

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);
  }

Solution 2:[2]

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');

Solution 3:[3]

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);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 TylerH
Solution 2 Christopher Moore
Solution 3 Samuel Howell