So far we only used print which isn’t particularly useful in Google Apps scripts. For real work we want to use the Google Apps APIs. We can do this by simply using Dart’s JS interop (don’t forget to run pub get ):

With this interop library we can recreate our initial example in Dart:

As before, we have to add the preamble again and copy paste the result into the Script Editor. Then we can run the dartPrint function which results in executing the main function as a side-effect, running the code to create a new file.

While we still have to fix this entry-point problem, we now have a way to write sophisticated Google Apps scripts in Dart. However, the work-flow isn’t really user friendly.

Image generated by a Dart script, adjusting the size and colors of some cells.

Better Tooling

Copy/pasting the output of dart2js into the editor works, but is way too annoying. One can automatize the copying of the generated code into the editor with a Chrome extension, but fortunately there are even better ways.

The Google Drive API supports creating new scripts through its REST API. I have written an open-source tool that takes a dart2js generated files and uploads them into Google Drive as a Google Apps script. That script even continues to watch the input file and updates the apps script every time it changes.

ᐅ pub global activate apps_script_tools

ᐅ apps_script_watch hello.js hello

Need to create new project.

Creating new file hello

Uploading hello done

Need to update existing project.

Updating existing file hello

Uploading hello done

This example shows how, after the initial creation, the watch-program continues to update the apps-script on the server.

Note that the apps_script_watch script only needs the dart2js-output. It automatically adds the necessary preamble.

Different Entry Points

The last remaining obstacle we face is defining the entry-points of our script. As before we will use JS-interop to set this up. The idea is simple: we create a JavaScript stub function, that we then overwrite from within Dart. This involves a bit of boilerplate code, but only needs to be done for entry points (of which a program usually only has a few).

In the JavaScript code we simply add a new empty function for every entry-point we want to expose:

Since we don’t want to hand-edit the generated dart2js output, this step can be automated with apps_script_watch (or gsify ) by using the -s flag, which automatically adds these empty stub functions.

ᐅ apps_script_watch -s createHello -s createGoodBye hello.js hello

In Dart we overwrite these as follows:

As can be seen, we treat the stub functions as JavaScript setters we then overwrite in the main function. From that moment on, all calls to these functions will run our Dart code.

Google Apps Package

Creating the interop libraries for Google Apps is straight-forward, but for convenience I have published the libraries I wrote as the google_apps pub package. It is hand-written and only contains the functionality I actually needed.

Eventually I might want to auto-generate the libraries, but for now it’s really bare-bones and I am happy to accept pull-requests.

Bound Scripts / Add-ons

Scripts that should be run on an opened document / spreadsheet (inside the editor) are called “bound scripts” or “add-ons”. Bound scripts are scripts that are specific to a single file (spreadsheet, doc, …) and are created when the user opens the script editor from the document’s editor (usually Tools , Script Editor... ).

Add-ons are installed in the editor and are thus available for all documents the editor supports.

In addition to accessing the opened document (for example with SpreadsheetApp.getActiveSheet() ), these scripts can run code when the document is opened. This makes it possible to add menu entries to the editor. The following program adds a menu-entry that opens a small dialog box:

This program must be executed as bound script or add-on. To run the code as an add-on, the script editor has a useful testing functionality: Run , Test as add-on . Not only is this approach good for developing an add-on that should get eventually published, it is also great for one-time scripts that don’t need to stick with the document. The biggest limitation of this approach is that unpublished scripts can’t be shared with other users. There are no private add-ons (except for domains).

The easiest way to share unpublished scripts is to use the script as shared library. As soon as the scrip has a version ( File , Manage versions ) other scripts can include the shared library with a prefix ( Resources , Libraries ). One can then create a bound script and just forward the entry-points. For example, for the hello_docs.dart example we could create the following forwarder bound-script (assuming the shared library was imported with the prefix identifier dart ):

More information can be found in the README of the apps_script_tools package.

More Examples

The Google Apps package has a few more examples: https://github.com/google/dart_google_apps/tree/master/example

Conclusion

Writing Google Apps scripts in Dart is easy and fun. I recently needed to develop a small spreadsheet application to manage a tournament, and am fully sold on writing Google Apps scripts. While the refresh-cycle is not getting close to the one of Flutter it is fast enough that it doesn’t get in the way. Due to the spreadsheet nature it also doesn’t lose state.

Have fun writing Google Apps scripts in Dart.