2 min read

Categories

Tags

File URIs and Content URIs, oh my.

I created an app that will recite the traveler’s prayer for you.

woman reading aloud from a book

Like that.

Part of the point is that you can use your own recording of the traveler’s prayer instead of hearing my voice. So I need:

  1. Some way to pick an audio file.
  2. Copy it into my app file directory, so that the app can continue to use it even if the original file is moved or deleted.
  3. Use it in my app.

Use it in app

That’s pretty easy. I save only one custom app file, so that I’m not overrun with custom recordings. Surprisingly, I call it custom.mp3. I play it if needed. Done.

Or not… I would like to see the name of the file I’m using at least — much nicer to see my_recording.mp3 or cool_version.mp3 rather than custom.mp3. So I also save in SharedPreferences the name of the file.

dragon in first ledge

One step done to get to the pink smiley

Pick an audio file

The great file_picker plugin is perfect for this, as you can set it to pick only audio files. It works on all platforms and has multiple options.

Easy-peasy.

dragon in second ledge

Second step done to get to the pink smiley

Copy file to app directory

File has a built in function copy that copies the current file to a new place. So I figured I would use that.

The FilePicker doesn’t return a File, it returns a PlatformFile. PlatformFile has no copy function.

OK, no problem! PlatformFile has an identifier, which is a Uri on Android. And File has a File.fromUri option. So all we need to do is:

FilePickerResult? result = await FilePicker.platform.pickFiles();

if (result != null) {
  PlatformFile file = result.files.first;
  File toCopy = File.fromUri(Uri.parse(file.identifier));
  toCopy.copy("my/directory/custom.mp3")
}

Right?

Error:Cannot extract a file path from a content URI

Apparently, File can parse file://URIs (which makes sense). The FilePicker returns a content:// URI on Android.

Sooo, I looked around, and the recommended method is another plugin: uri_to_file. Which allows me to …yep, get a file from my content URI.

So a perfectly simple action becomes:

FilePickerResult? result = await FilePicker
    .platform
    .pickFiles(type: FileType.audio);//file_picker plugin
if (result != null) {
  PlatformFile file = result.files.first;
  //need path_provider to get the app directory
  final directory =
      await getApplicationDocumentsDirectory();
  //get file from content uri - uri_to_file plugin
  File pickedFile =
      await toFile(file.identifier!);
  //and finally
  File cachedFile = await pickedFile.copy(
      "${directory.path}${Platform.pathSeparator}custom.mp3");

For these four lines, I needed 3 plugins: file_picker, path_provider and uri_to_file.

I’m sure that I’m missing something, does anyone have a better way?

On the other hand, it’s working great :) Check out the source code on GitHub.

dragon got the prize!

Got the smiley!

vines-separator

Images created using Fooocus 2.1.824 and edited in GIMP.

In these terrible times, we need all the prayers we can get. #BringThemHomeNow.

Check out my free and open source online game Space Short. If you like my site, you can also buy me a coffee.