Working with JavaScript in Webview Flutter

Connel Asikong
6 min readJul 14, 2021

Hello there, this is actually the continuation or PT 2 of: This, where I showed you how to display your HTML in Webview on Flutter. Today I woke up and decided why not also show them how to add Javascript and also get data from Javascript to Flutter, I mean that’s important right?

Well, I’ll not be teaching you Javascript but I am just going to show you around town but before we start… I need some coffee

Photo by Clay Banks on Unsplash

Now I feel the coffee in my veins…

Okay let’s start, open Webview.dart

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class Webview extends StatefulWidget {
@override
_WebviewState createState() => _WebviewState();
}

class _WebviewState extends State<Webview> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
WebViewController _con;

/*
*
* Webview
* */

String setHTML(String email, String phone, String name) {
return ('''
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>

<body style="background-color:#fff;height:100vh ">

<div style="width: 50%; margin: 0 auto;margin-top: 200px">
<table class="table table-striped">
<tbody>
<tr>
<th>Name</th>
<th>$name</th>
</tr>
<tr>
<th>Email</th>
<td>$email</td>
</tr>
<tr>
<th>Phone</th>
<th>$phone</th>
</tr>
</tbody>
</table>
<a type="button" class="btn btn-success" style="width: 210px" href="https://connelevalsam.github.io/connelblaze/">
Submit
</a>
</div>
</body>
</html>


''');
}

_printz() => print("Hello");

_loadHTML() async {
_con.loadUrl(Uri.dataFromString(
setHTML(
"connelblaze@gmil.com",
"+2347034857296",
"Connel Asikong"
),
mimeType: 'text/html',
encoding: Encoding.getByName('utf-8')
).toString());
}


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Webview'),
),
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
// _controller.complete(webViewController);
_con = webViewController;
_loadHTML();
},
onProgress: (int progress) {
print("WebView is loading (progress : $progress%)");
},
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
onPageStarted: (String url) {
print('Page started loading: $url');
},
onPageFinished: (String url) {
print('Page finished loading: $url');
},
gestureNavigationEnabled: true,
);
}),
);
}

}

This was what we had. Now what are we going to be doing today? Well I say we use the JS Fetch() to get data from an API and display on Webview then send it to a Flutter Widget — but first, let’s get JS to work. We need to add this function:

JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
// ignore: deprecated_member_use
print(message.message);
});
}

See the JavascriptChannel method is used to add JS into our Webview and it has a name: Toaster. That’s the object we will be using. You can name it whatever as long as you reference it when you make calls from JavaScript to Flutter.

To be able to use our JS in webview, we must add another property:

javascriptMode: JavascriptMode.unrestricted,

So your webview will be like this:

WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
// _controller.complete(webViewController);
_con = webViewController;
_loadHTML();
},
...

Now lets go back to our HTML and add another button and a h2:

<button type="button" class="btn btn-info" onclick="hello()" style="width: 210px">
Hello
</button>
<h2>This is my JS</h2>

Then inside our <script> we will create the JS function to change the h2 text:

function hello() {
header.innerText = 'Hello';
}
Image by CB

Yohohoho!!! We did it! So on tap of the Hello button, the h2 text changes, but can we also send something back to Flutter? Yes we can! We are the CB soldiers! Now remember our JavascriptChannel method, it has an object name Toaster. Using that, we can add it to our hello():

function hello() {
header.innerText = 'Hello';
Toaster.postMessage("Hello from the other side");
}

Check your Flutter console, you should see that.

Now I’m talking too much, don’t blame me, blame the coffee!
I’ll be using this API and I won’t be explaining anything in JS, if you want to understand what’s going on I say you check out Digital Ocean; They really good. At the end of this, you can get it from my GitHub, I’ll share the link below. The API shows 10 names of random Authors, using JS Fetch() we’ll now get and display, so create some functions in JS:

function createNode(element) {
return document.createElement(element);
}

function append(parent, el) {
return parent.appendChild(el);
}

function getAuthors() {
fetch(url)
.then((res) => res.json())
.then(function(data) {
let authors = data.results;
return authors.map(function(author) {
let li = createNode('li');
let img = createNode('img');
let span = createNode('span');
img.src = author.picture.medium;
span.innerHTML = author.name.first +" "+ author.name.last;
append(li, img);
append(li, span);
append(ul, li);
ul.style.padding = '1em';
})
})
.catch(function(error) {
console.log(error);
});

Then create the ul in the HTML:

<div>
<h1>Authors</h1>
<ul id="authors"></ul>
</div>
//then edit the hello button
<button type="button" class="btn btn-info" onclick="getAuthors()" style="width: 210px">
Authors
</button>
Image from CB

Before I go forward, I’d like to add a popup when the JS is done fecthing. So that when you tap on continue it takes you to the next page.

void showAlertDialog(BuildContext context, String msg, {bool type = false}) {
// set up the button
Widget okButton = TextButton(
child: Container(
color: Colors.green,
padding: EdgeInsets.all(10.0),
child: Text("Continue", style: TextStyle(color: Colors.white),)
),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
Future.delayed(const Duration(milliseconds: 500), () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => Authors(res: details,)));
});
},
);

Widget noButton = TextButton(
child: Container(
color: Colors.red,
padding: EdgeInsets.all(10.0),
child: Text("Cancel", style: TextStyle(color: Colors.white),)
),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
);

// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text((
type == true ? "Message" : "Error"
)),
content: Text(msg),
actions: [
noButton,
okButton,
],
);

// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}

then call it:

JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
// ignore: deprecated_member_use
details.add(message.message);
print(details);
setState(() {
isDone = true;
});
if (isDone == true) {
showAlertDialog(context, "Proceed", type: true);

setState(() {
isDone = false;
});
}
});
}

So now let’s create another widget to display this Authors, I have Authors.dart.

Remeber we’re getting: first, last names and an Image, 3 items. Now this delayed me a bit coupled with work but Eternity Code helped me out:

var result;
List j = [];
@override
void initState() {
super.initState();
result = widget.res;
print("=x=x From authors - $result"); //gets the total authors gotten
int i = 1;
int z = result.length;
int split = 3; //how many items per each author
for (int i = 0; i < z; i += split) {
var end = (i + split < z) ? i + split : z;
j.add(result.sublist(i, end));
}

// print(result);
print("Value of j");
print(j);
print("Single Value of K");
print(j[0][1]);
}
Image by CB

That’s how the end product looks like!

I’m tired of typing, full code in my GitHub.
Shout out also to bla_k for the motivation to do this without MORE coffee… I’m so sorry mother programmer, I have failed you! :(

Anyway, this was fun, till next time…which is soon, keep coding!

Buy me Coffee

--

--

Connel Asikong

Programmer, Designer, Promoter, music lover, God fearer. thrilled about technology. wish I could help and do more.