Using JavaScript in Flutter Web

JT Liew
4 min readApr 19, 2020

Is there a hot JavaScript library that you want to use in Flutter Web but there is no equivalent for it in Dart? You are in luck! Dart was originally a language for internet browsers to begin with so they have this sweet Dart package called the js which you can use to interop between JavaScript and Dart.

To provide an example, we want to access this TypeScript class on the Flutter side. Let’s create a Dog.ts file as shown below. We have

  1. A constructor
  2. Properties of name and age of type string and number respectively.
  3. Getters and Setters for name and age.
  4. A barkmethod
  5. A jump method that receives a function parameter that takes in height of type number
  6. A sleep method that takes in an object.
// Dog.tsclass Dog {
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
get name(): string {
return this._name;
}
get age(): number {
return this._age;
}
bark() {
console.log(`${this._name}:${this._age}:: Woof!`)
}
jump(func: (height: number)=>void){
func(20)
}
sleep(options: {bed: boolean, hardness: string}){
if (options.bed){
console.log(`${_name} is sleeping on a ${options.hardness} bed.`)
} else {
console.log(`${_name} is sleeping on the floor. :(`)
}
}
}

Afterwards, we’ll convert this TypeScript file into JavaScript file with

tsc --target ES5 Dog.ts. With this done we’ll have a Dog.js file alongside Dog.ts.

Note that --target ES5 is required as according to the answer provided in the Stackoverflow post here:

“Normally JS interop would rely on function hoisting to ensure that an old-style JS class was in scope. ES6 classes however aren’t hoisted and aren’t available where Dart expects them to be. ”

We’ll leave this aside for now and we’ll create an empty Flutter project and import the js library in the pubspec.yamllike so:

dependencies:
flutter:
sdk: flutter
js: ^0.6.1

Note: Use Flutter Beta to enable the development of Flutter Web.

We’ll create an empty Dart file called Dog.dart. Inside this Dog.dart class, we’ll have our Dart <-> JS interop code.

@JS()
library dog;
// The above two lines are required
import 'package:js/js.dart';@JS()
class Dog {
external Dog(String name, int age);
external String get name;
external int get age;
external void bark();
external void jump(Function(int height) func);
external void sleep(Options options);
}@JS()
@anonymous
class Options {
external bool get bed;
external String get hardness;
external factory Options({bool bed, String hardness});
}

the @JS() annotation comes with the js package which annotates our Dog class in Dart to interop with the JavaScript’s Dog class. The external is put in front of the constructors, methods as well as the getter and setters.

If we want to have a custom name for the class in the Dart side. we can name it differently if we add a string as a parameter in the JS() annotation like shown below, although it is discouraged according to the comments in the js package.

@JS("Dog")
class DartDog {
...
}

JavaScript objects as a parameter

To pass a JavaScript object as a parameter. We’ll need to create a class and annotate it with @JS() and @anonymous. We will use the Options class as the example shown below.

@JS()
@anonymous
class Options {
external bool get bed;
external String get hardness;
external factory Options({bool bed, String hardness});
}

And with that, we can pass the Options class to the sleep method like so

@JS()
class Dog {
...
external void sleep(Options options);
}

Passing Functions to JavaScript

For the function parameter, we’ll need to wrap our Function with allowInterop like this

dog.jump(allowInterop((int height){
print(height);
}));

Hooking things up

To make this work, copy the generated Dog.js from the TypeScript file into the web folder of the Flutter project alongside index.html. Next, open up index.html and add this line before the import main.dart.js script tag.

<script src="Dog.js" type="application/javascript"></script>

A full example of the index.html is shown below:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="ts_app">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="shortcut icon" type="image/png" href="favicon.png"/>
<title>ts_app</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<!-- Add the import Dog.js script tag here here -->
<script src="Dog.js" type="application/javascript"></script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

We are nearly there, create a widget with a button in the centre of the screen which will use the Dog class.

class SomeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Flutter2JS"),),
body: Center(
child: RaisedButton(
onPressed: (){
var dog = new Dog("Bear", 12);
dog.bark();
print(dog.age);
print(dog.name);
dog.jump(allowInterop((int height){
print(height);
}));
dog.sleep(Options(bed: true, hardness: "Soft"));
},
),
),
);
}
}

After that, we can create and call our JavaScript Dog class in Flutter Web!

We can see the Dog class is printing out in the console log.

Thank you for reading!

--

--

JT Liew

Mobile developer; Netvirta; Android; Flutter; Gradle; Kotlin