How to get the file path to an asset included in a Dart package?

1.4k views Asked by At

I am writing a Dart package (not Flutter). I have included a few bitmap images as public assets, e.g., lib/assets/empty.png. When this package is running as a command-line app for an end-user, how can I get the file path to these assets on the user's system?

Use-case: My Dart package calls out to FFMPEG, and I need to tell FFMPEG where to find these asset files on the system that's using my package. For example, the call to FFMPEG might look like:

ffmpeg -i "path/to/lib/assets/empty.png" ...
2

There are 2 answers

0
SuperDeclarative On BEST ANSWER

Accessing a Dart package's assets can happen in two modalities:

  1. Running a Dart CLI app with the dart tool and accessing a dependency's assets, or
  2. Running an executable CLI app

The difference between these two situations is that when you're running a CLI app using the dart tool, all of your dependencies are available as structured packages in a local cache on your system. However, when you're running an executable, all relevant code is compiled into a single binary, which means you no longer have access at runtime to your dependencies' packages, you only have access to your dependencies' tree-shaken, compiled code.

Accessing assets when running with dart

The following code will resolve a package asset URI to a file system path.

final packageUri = Uri.parse('package:your_package/your/asset/path/some_file.whatever');
final future = Isolate.resolvePackageUri(packageUri);

// waitFor is strongly discouraged in general, but it is accepted as the
// only reasonable way to load package assets outside of Flutter.
// ignore: deprecated_member_use
final absoluteUri = waitFor(future, timeout: const Duration(seconds: 5));

final file = File.fromUri(absoluteUri);
if (file.existsSync()) {
  return file.path;
}

This resolution code was adapted from Tim Sneath's winmd package: https://github.com/timsneath/winmd/blob/main/lib/src/metadatastore.dart#L84-L106

Accessing assets when running an executable

When compiling a client app to an executable, that client app simply cannot access any asset files that were stored with the dependent package. However, there is a work around that may work for some people (it did for me). You can store Base64 encoded versions of your assets in your Dart code, within your package.

First, encode each of your assets into a Base64 string and store those strings somewhere in your Dart code.

const myAsset = "iVBORw0KGgoAAA....kJggg==";

Then, at runtime, decode the string back to bytes, and then write those bytes to a new file on the local file system. Here's the method I used in my case:

/// Writes this asset to a new file on the host's file system.
///
/// The file is written to [destinationDirectory], or the current
/// working directory, if no destination is provided.
String inflateToLocalFile([Directory? destinationDirectory]) {
  final directory = destinationDirectory ?? Directory.current;   
  final file = File(directory.path + Platform.pathSeparator + fileName);

  file.createSync(recursive: true);
  final decodedBytes = base64Decode(base64encoded);
  file.writeAsBytesSync(decodedBytes);

  return file.path;
}

This approach was suggested by @passsy

0
Brett Sutton On

Have a look at the dcli package.

It has a 'pack' command designed to solve exactly this problem.

It encodes assets into dart files that can be unpacked at runtime.