Skip to main content

Flutter WebView Communication with JavaScript

· 4 min read

When developing a project with flutter, you may need to load the H5 page. Opening the H5 page in mobile development requires the WebView. At the same time, in order to communication with WebView, it is sometimes necessary to use JSBridge to realize the communication between the native and WebView.


webview_flutter is a flutter plugin that provides a WebView widget. In order to use webview_flutter, you need to import the plugin in your pubspec.yaml file.

  • add network permissions to Android.
<uses-permission android:name="android.permission.INTERNET" />
  • if you want to open http pages on ios, add the following code to the ios/Runner/Info.plist file.

webview_flutter Usage

WebView constructor is as follows:

Key key,
this.onWebViewCreated, // webview created callback
this.initialUrl, // initial url to load
this.javascriptMode = JavascriptMode.disabled, // javascript mode, default is disabled
this.javascriptChannels, // javascript call flutter channel
this.navigationDelegate, // navigation delegate to intercept navigation
this.gestureRecognizers, // gesture recognizers
this.onPageStarted, // page started callback
this.onPageFinished, // page finished callback
this.onWebResourceError, // web resource error callback
this.debuggingEnabled = false, // enable webview debugging, default is false
this.gestureNavigationEnabled = false, // enable webview gesture navigation, default is false
this.userAgent, // user agent
this.initialMediaPlaybackPolicy =

Load local html file or network files

In order to easy to use it, we will generally carry out secondary packaging, mainly interface and function packaging. Here is a WebViewPage that I have wrapped to load html or network files.

class WebViewPage extends StatefulWidget {

String url;
final String title;
final bool isLocalUrl;

WebViewController _webViewController;

WebViewPage({this.url, this.isLocalUrl = false, this.title});

_WebViewPage createState() => _WebViewPage();

class _WebViewPage extends State<WebViewPage> {

JavascriptChannel jsBridge(BuildContext context) => JavascriptChannel(
name: 'jsbridge', // same as the name in the html file
onMessageReceived: (JavascriptMessage message) async{

Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppbar(),
body: _buildBody()

_buildAppbar() {
return AppBar(
elevation: 0,
backgroundColor: Color(0xccd0d7),
title: Text(widget.title, style: TextStyle(color:,),
centerTitle: true,
leading: IconButton(icon: Icon(Icons.arrow_back, color: Color(0xFF23ADE5),), onPressed: () {

_buildBody() {
return Column(
children: <Widget>[
height: 1,
width: double.infinity,
child: const DecoratedBox(decoration: BoxDecoration(color: Color(0xFFEEEEEE))),
flex: 1,
child: WebView(
initialUrl: widget.isLocalUrl ? Uri.dataFromString(widget.url, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
.toString(): widget.url,
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: <JavascriptChannel>[
onWebViewCreated: (WebViewController controller){
widget._webViewController = controller;
controller.canGoBack().then((value) => debugPrint(value.toString()));
controller.canGoForward().then((value) => debugPrint(value.toString()));
controller.currentUrl().then((value) => debugPrint(value));
onPageFinished: (String value){
.then((title) => debugPrint(title));

// load local html file
_loadHtmlAssets(WebViewController controller) async {
String htmlPath = await rootBundle.loadString(widget.url);
controller.loadUrl(Uri.dataFromString(htmlPath,mimeType: 'text/html', encoding: Encoding.getByName('utf-8')).toString());

When used, you just to in the corresponding attribute. Note that when loading the local html file, you declare the html file in the pubspec.yaml file, as shown below.

// ...
- assets/real_data_help.html

We can then load the local html file using the wrapped component. such as:

class MyApp extends StatelessWidget {

String localUrl = 'assets/real_data_help.html';

Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
home:WebViewPage(url:localUrl, isLocalUrl: true, title: 'Local file title'),

JS call Flutter


javascriptChannels is used to communicate with the native. For example, there is the following JS code.

<button onclick="callFlutter()">callFlutter</button>;
function callFlutter() {

Note that the Toast must same as the JavascriptChannel params name.

javascriptChannels: <JavascriptChannel>[

JavascriptChannel _alertJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toast',
onMessageReceived: (JavascriptMessage message) {

Flutter call JS

WebViewController _webViewController; // store the webview controller

// ...
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
// get to the webviewController and then call the method below
_webViewController = webViewController;

// call _webViewController evaluateJavascript to call js
_webViewController.evaluateJavascript("Toast.postMessage('Hello world');");