How to save to a particular folder?

How to save an mp3 file to a desired folder in android?

Hello and welcome!

You can write files in the temporary directory (cache) or the app-specific document directory only, it’s not possibile to write files to other paths.
More details in this Flutter documentation page: Read and write files .

Is it true?, But i need in user accessible location not in the android folder.
Is there any other way?

Thank you.

The official Flutter path_provider package currently support only those two.

I made a brief search and found that maybe you could try this one, which seems to provide access to the download shared folder.

I didn’t find something similar to access the media shared folder yet. though it could exist! I’ll give another look…

hth

1 Like

Thanks for your time i will look into it…

You are welcome, I’m in the learning/exploring phase so it’s a pleasure to look for possible solutions for future projects requirements!
I went on searchin in pub.dev for something related to shared media path but wasn’t able to spot anything appropriate. I’m tempted to try building it myself, using the source code of that downloads_path_provider package as a guideline, after a first rapid glance it does not look too hard, given the possibility to access the android shared media path. If I only had a 28h/day… :sweat_smile:

Even i am also in the same phase .I just started 2 weeks ago

So, out of curiosity I’m digging into the source code of Flutter’s path_provider package, and I’m seeing some interesting lines in it.

in PathProviderPlugin.java at lines 64-68 of onMethodCall() there is this case:

  case "getExternalStorageDirectories":
    final Integer type = call.argument("type");
    final String directoryName = StorageDirectoryMapper.androidType(type);
    result.success(getPathProviderExternalStorageDirectories(directoryName));
    break;

Then in StorageDirectoryMapper.java, in androidType(..)

this switch:

  switch (dartIndex) {
      case 0:
        return Environment.DIRECTORY_MUSIC;
      case 1:
        return Environment.DIRECTORY_PODCASTS;
      case 2:
        return Environment.DIRECTORY_RINGTONES;
      case 3:
        return Environment.DIRECTORY_ALARMS;
      case 4:
        return Environment.DIRECTORY_NOTIFICATIONS;
      case 5:
        return Environment.DIRECTORY_PICTURES;
      case 6:
        return Environment.DIRECTORY_MOVIES;
      case 7:
        return Environment.DIRECTORY_DOWNLOADS;
      case 8:
        return Environment.DIRECTORY_DCIM;

All this make me think that maybe we could use path_provider to access all the above shared directories.
Calling getExternalStorageDirectories with a proper integer type…
I didn’t studied the code enough, nor I’m an expert of Flutter Method Channels at all, so I’m not sure I understand it well yet, but it looks good. I’m puzzled there is no trace of that in the documentation though.
I’ll do some experiment myself and let you know about.

1 Like

Thank you very much for your information, i will also look into that.

The pub.dev page has an example main.dart showing path_provider usage to get those external data directories, I tried to write some proof of concept code out of it, but couldn’t make it work, something with the return value of getExternalStorageDirectories(type) being a Future<List<Directory>> and me not capable of getting the path out of it (also I’m still struggling with Future I guess). I’ll let you know if I get to something that works :slight_smile:

Later edit: the following video helped me a lot in understanding the above mentioned issue I had understanding futures, now i get those FutureBuilder widgets!

1 Like

Thanks so much , i will also try to do it…

I hacked together a simple proof of concept:

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'External Path Provider',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'External Path Provider'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<List<Directory>> _getExternalStoragePath() {
    return getExternalStorageDirectories(type: StorageDirectory.documents);
  }

  String _fileFullPath;

  Future _writeExternalStorage() async {
    final dirList = await _getExternalStoragePath();
    final path = dirList[0].path;
    final file = File('$path/dummy_doc.txt');

    file.writeAsString('I am the dummy_doc dot txt.').then((File _file) {
      setState(() {
        _fileFullPath = _file.path;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Column(
          children: <Widget>[
            RaisedButton(
              child: Text('Write to External Storage'),
              onPressed: () {
                _writeExternalStorage();
              },
            ),
            Text("Written: $_fileFullPath"),
          ],
        ));
  }
}

In my Huawei device, without an external SD-card or SIM, it ends up writing the dummy text file in Android/data/com.example.extpath/files/Documents

Schermata 2020-05-11 alle 19.15.30

which, disappointingly but reasonably, I could not find with the device’s file manager app in Documents. More experiments needed.

Edit: I’ll try to add the read permissions for the external storage, because I remembered now that I gave only write one:

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

even if i think it is intended for the app to read and not the others. Anyway it worths a try.

Even i am able to store in android folder ,in com.example.projectName. The problem here is i need it outside the android folder.It should be accessible and viewed by the user.

Thank you.

Yes, we are back at the starting point.
The Flutters’ Read and write documentation states:

The path_provider package provides a platform-agnostic way to access commonly used locations on the device’s file system. The plugin currently supports access to two file system locations:

Temporary directory

Documents directory

So, I’d try that downloads_path_provider package at this point; maybe it can do the trick and let you save in a shared path, accessible by other applications, even if it is not the right destination (I recall you wanted to save .mp3, then they should go in Music, or something alike).

I’m really puzzled by the code in path_provider anyway because it seems to handle those shared directories, but it actually doesn’t.

So, downloads_path_provider will solve the problem?

Oh, I didn’t try it, but I guess you should as it promises to…

get the download directory for Android.

Also if you get it working please report here, it will be helpful for other users too.

Edit: oh well… I’m too curious to wait, I’m going to test and let you know about it.

The following proof of concept code for downloads_path_provider does work! Notice that I had an issue with permission denied even after adding the manifest permissions (read and write), so I went to the device’s Settings -> App -> Permissions -> Storage and enable it for my test app (it was disabled!)
then it could write a dummy.txt file to Download path, and I could retrive it with the Download default app.
Here is the code for it (remember adding the permissions to your AndroidManifest.xml too).

    import 'dart:async';
    import 'dart:io';

    import 'package:flutter/material.dart';
    import 'package:downloads_path_provider/downloads_path_provider.dart';

    void main() {
      runApp(MyApp());
    }

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Download Path Provider',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Download Path Provider'),
        );
      }
    }

    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
      final String title;

      @override
      _MyHomePageState createState() => _MyHomePageState();
    }

    class _MyHomePageState extends State<MyHomePage> {
      Future<Directory> _getDownloadsPath =
          DownloadsPathProvider.downloadsDirectory;

      String _feedbackMsg = '';

      Future _writeToDownloadPath() async {
        final downloadsDir = await _getDownloadsPath;
        final downloadsPath = downloadsDir.path;
        final file = File('$downloadsPath/Dummy.txt');

        file.writeAsString('I am Dummy.txt').then((File _file) {
          setState(() {
            _feedbackMsg = "Done!";
          });
        });
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Column(
              children: <Widget>[
                RaisedButton(
                  child: Text('Download Dummy.txt'),
                  onPressed: () {
                    _writeToDownloadPath();
                  },
                ),
                Text(_feedbackMsg),
              ],
            ));
      }
    }
1 Like

Thanks for POC i will try to use it