Interop Between Flutter Web App and JS Library
2021-02-14tl;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
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,
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
intoObject
. I prefer to make a type just to denote JSPromise
@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.