File URIs and Content URIs, oh my.
I created an app that will recite the traveler’s prayer for you.
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:
- Some way to pick an audio file.
- 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.
- 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.
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.
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.
Got the smiley!
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.