Flutter Web — Calling Custom Java Script functions in Dart & Vice Versa

Piumi Maheshika
4 min readSep 10, 2020

--

For my recent project, I had to create a web application in flutter and load it inside a Mobile wrapper. One requirement of this app is to connect to a Bluetooth device and read data. For instance, when user clicks a button on web view, a function should be triggered in Mobile wrapper to scan for available bluetooth devices and connect to a specific device. Once the device is connected, the result should be passed back to web view and display to the user.

My challenge was implementing this inter-communication between Flutter web and Mobile app. Obviously Java Script was the choice. According to my design, I had to use Java Script functions inside my dart code and dart code inside Java Script.

I had a hard time finding a good tutorial on how to this 😫 but eventually I could come up with a solution for my self 😄.

This article will show you, how to achieve this. Hope it will save your time.

Again my Requirement is

  • Java Script to trigger the Mobile to connect to a Bluetooth device
  • JavaScript to receive data from Mobile and display in web.

Let’s start with Flutter Web Application first.

Flutter Web

Add below library into your project’s pubspec.yaml file.

dependencies:
js: ^0.6.1

Now create a new javascript file in project’s web directory. I name it common.js.

//----1---//
function connectDevice() {
ConnectBLE.postMessage('ble.connect');
}
//----2---//
function receiveDeviceStatus(text){
console.log('*** Update Device Status ***');
}

As names imply,

  • 1st function is used to trigger connecting to a Bluetooth device
  • 2nd function will receive connection status data

Then add the Javascript file reference to index.html in the web directory.

<script defer src="js/common.js"></script>

It is time to work on the interoperability tasks.

Create a new dart file in the project’s lib directory. I call this common.dart.

All interop code needs to be annotated with the “@JS()” annotation and it must have a library. This will allow us to use JavaScript namespaces and classes. Here I am going to use the javascript functions I created in the previous step.

@JS()
library jquery;
//----1---//
@JS('connectDevice')
external void connectDevice();
//----2---//
@JS()
external void receiveDeviceStatus(dynamic text);
@JS('updateDeviceStatus')
external set pReceiveDeviceStatus(void Function(String value) f);
void pNotifyDeviceStatus(BuildContext context, String value) {
Provider.of<BluetoothProvider>(context, listen: false).setDeviceStatus(value);
print('*** Device Connection status $value ***');
}

Let’s walk through the Section 1 of the code.

  • Having the @JS(‘connectDevice’) will allow us to call connectDevice function inside our dart code. For example, for onPressed event of a button.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Connect to Bluetooth Device'),
),
body: Center(
child: RaisedButton(
child: Text('CONNECT..'), onPressed: () => connectDevice()),
),
);
}

Now as you can see for Section 2, I have got more code. This is to serve the purpose of passing some data from Mobile Wrapper to web. In our case, passing connection status back to web and display it.

Here is how we make a dart function callable from Java Script.

  1. Pass Dart function to Java Script API as an argument.
@JS('updateDeviceStatus')
external set pReceiveDeviceStatus(void Function(String value) f);

2. Define the function that needs to be called inside Java Script.

void pNotifyDeviceStatus(BuildContext context, String value) {
Provider.of<BluetoothProvider>(context, listen: false).setDeviceStatus(value);
print('*** Device Connection status $value ***');
}

To update UI, we need to change the state in flutter. Here I m using Provider package to do state management. (This post will not cover the Provider package in detail).

3. Wrap the Dart function using allowInterop().

I have added this line inside main() method in project’s main.dart file.

pReceiveDeviceStatus = allowInterop((text) => pNotifyDeviceStatus(context, text));

4. To make a Dart function callable from JavaScript by name, use a setter annotated with @JS().

@JS()
external void receiveDeviceStatus(dynamic text);

That is all for Flutter web.

Now we will look into the Mobile wrapper. Have I forgot to mention that my Mobile wrapper is also using Flutter ?😎

Flutter Mobile Wrapper

Add the below dependency in Mobile wrapper project’s pubspec.yaml file.

dependencies:
flutter_webview_plugin: ^0.3.11

Create a new webview.dart file as below and load it in inside main.dart.

import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:flutter/services.dart';
class Webview extends StatefulWidget {
final String url = '<url_to_load>';
@override
_WebviewState createState() => _WebviewState();
}
class _WebviewState extends State<Webview> {
final flutterWebviewPlugin = new FlutterWebviewPlugin();
@override
void dispose() {
flutterWebviewPlugin.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final Set<JavascriptChannel> jsChannels = [
JavascriptChannel(
name: 'ConnectBLE',
onMessageReceived: (JavascriptMessage message) {
_connectToBluetoothDevice();
}),
].toSet();
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
return Scaffold(
body: SafeArea(
child: WebviewScaffold(
resizeToAvoidBottomInset: true,
clearCache: true,
url: widget.url,
withJavascript: true,
withZoom: false,
mediaPlaybackRequiresUserGesture: false,
hidden: false,
javascriptChannels: jsChannels,
),
),
);
}
}
void _connectToBluetoothDevice(){
....
....
}

Briefly what we do here is, load the url (that our web application is hosted) inside the webview and register javascript channels to listen to java script events.

Once the java script function (connectDevice from web) is called, it will trigger the _connectToBluetoothDevice function defined inside wrapper.

Ok 👊 first part is done. Let’s see how to pass some data from Wrapper to web.

Let’s call the Java Script function receiveDeviceStatus() inside _connectToBluetoothDevice() function to pass data.

void _connectToBluetoothDevice(){
...
...
flutterWebviewPlugin.evalJavascript
('receiveDeviceStatus(CONNECTION_STATUS);');
}

That is it 💪. Now we have a working solution of passing data back and forth between Flutter Web and Mobile application using Java Script.

--

--

Responses (3)