Interop Between Flutter Web App and JS Library

tl;dr Dart JS Interop allows Flutter Web Apps to use third party services even they don't have SDK for it. This article will go through how to interop some common JS library calls.

Background

Gru's plan failed since no Flutter Web SDK.

I was working on a Flutter web app, and integrating with Auth0 is a good choice for user registration, login, etc.

However, Auth0 don't have SDK and examples for Flutter web. Should I write all the calls to Auth0 my own, or give up Auth0 and write all functions I needed from Auth0?

Solution

Since dart code can call JS on Dart web platform,

Think about it, maybe we still can use Auth0 in the Flutter web app.

our Flutter web app can also call Auth0 JS library in browser.

How to call Auth0 from dart in browser?

For this, you can check the auth0_flutter_web package on pub.dev, and its GitHub repo. Welcome to like, star and make pull requests.

How to call JS code from dart?

In the following, necessary setups and how to interop some common JS library calls will be covered.

1. Project Setup

Before we begin, add js dependency to pubspec.yaml,

dependencies:
  js: ^0.6.2

and load the JS library in index.html under web folder (besides lib).

<head>
  <!-- other head elements -->
  <script src="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></script>
  <!-- other head elements -->
</head>

2. "Headers" of .dart Files

The import statement is easy to understand. Besides, the library statement is a must, or else it won't compile. I did some search but not much explaination about this.

@JS()
library auth0_spa_sdk;

import 'package:js/js.dart';

3. Code

Invoke JS Functions

It is not too difficult. I just copy this from the package example.

// Calls invoke JavaScript `JSON.stringify(obj)`.
@JS('JSON.stringify')
external String stringify(Object obj);

Object Argument in JS Functions

Sometimes the JS function takes an object as argument.

function functionWithObjectArgument({intArg: 1, strArg: "boy"});

Since dart is a typed language, a class for the argument is needed.

@JS()
@anonymous
class ObjectArgument{
  external int get intArg;
  external String get strArg;

  external factory ObjectArgument({int intAgr, String strArg});
}

@JS()
void functionWithObjectArgument(ObjectArgument arg);

⚠ Fields can't be external, remember to mark it as getter with get.

JS Promise and Dart Future

They are the same thing for running asynchronous code, and the js package got this covered with promiseToFuture.

import 'package:js/js_util.dart'; // note this new import

@JS()
external Object delayedValue(Object value); // JS promise that return value later

print(await promiseToFuture(17)); // print 17

💡 It is confusing to assign JS Promise into Object. I prefer to make a type just to denote JS Promise

@JS()
@anonymous
class Promise<T>{} // just to denote a js promise object

@JS()
Promise<String> delayedValue(String value);

Closing

Flutter web is still in beta now, and it is expected not many integration SDK for it around. Fortunately, dart and JS interop opens up opportunities to use those services even they don't have Flutter Web SDK yet.

If you like this article, remember to show your support by buy me a book.