Dynamically load package contents with Dart's new Resource class

tldr: In Dart 1.12, you can now dynamically load the contents of files/assets from package dependencies. The new Resource class is currently implemented in the VM, with dart2js support coming in a future release.



Motivation

Dart applications are rarely just a collection of .dart files from a single developer. Real-world Dart apps often include numerous package dependencies, which contain additional Dart libraries as well as assets such as images, configuration files, template files, and more. It's always been possible to import third-party libraries with Dart's package: URI scheme, however prior to Dart 1.12 it was not possible to access non-Dart files via package: URIs.

For example, consider a tool such as stagehand, which generates new Dart projects such as web apps, server apps, pub packages, and more. The source of the new projects are stored as files and templates inside the stagehand package. Somehow, the stagehand tool needs to reference and load files from its packages. The contents of packages are addressable with package: URI scheme, but the Dart platform didn't provide a way to load those resources.

Hello, Resource!

Enter the new Resource class, which first appeared in Dart 1.12. The Resource class performs a very special role: allow Dart code to, at runtime, identify and load a resource identified by a package: URI.

Consider this simple package layout:

resourcetest/
  pubspec.yaml
  .packages
  bin/
    main.dart
  lib/
    myresource.txt

Here are the contents of main.dart:

main() async {
  var myResource = new Resource('package:resourcetest/myresource.txt');
  var resourceContents = await myResource.readAsString();
  print(resourceContents);
}

First, construct a new Resource with a package: URI. Of course, the Resource will only be able to load package: URIs from your application/script and dependencies declared in your pubspec.yaml file.

Then, call readAsString(), which returns a Future containing the contents of the resource.

That's it!

The Resource API

There are a few ways to load the contents of a Resource instance.

openRead() → Stream<List<int>>

Read the resource content as a stream of bytes.

readAsBytes() → Future<List<int>>

Read the resource content.

readAsString({Encoding encoding}) → Future<String>

Read the resource content as a string.

Technically, the Resource class can load any URI that it already knows how to load. For example, the Dart VM can load file: , http: , and package: URIs.

Things to consider

As of Dart 1.12, only the Dart VM has implemented the Resource class. Support is planned for dart2js in a near-future release.

The Resource class only works if your application is started with a valid way to resolve package: URIs. The Dart platform supports multiple ways to tell the runtimes and compilers where and how to resolve package: URIs, and under most circumstances, you don't need to do anything special.

One of the following must be true, in order for the Dart runtime to be able to resolve the package: URIs:


  • run your Dart app after it was installed with pub global activate
  • run your Dart app with a --package=path/to/.packages
  • run your Dart app with a --package-root=path/to/packages/dir
  • run your Dart app's entry point next to the .packages file
  • run your Dart app's entry point next to the packages/ directory


Normally, everything is set up for you, because pub does a great job of ensuring your app has a .packages file. This is true even with apps installed with pub global activate!

Summary

The new Resource class allows you to dynamically load the contents of package: URIs. You can use the new Resource class to load text or binary assets from within your app and your third-party dependencies.

Download a Dart 1.12-dev SDK and enjoy the new Resource class. We look forward to your feedback, and we can't wait to see what you build.

Popular posts from this blog

Lists and arrays in Dart

Converting Array to List in Scala

Null-aware operators in Dart