Flutter Webview two way communication with Javascript Flutter Webview two way communication with Javascript dart dart

Flutter Webview two way communication with Javascript


Here is an example of communication from Javascript code to flutter.

In Flutter build your WebView like :

WebView(              initialUrl: url,              javascriptMode: JavascriptMode.unrestricted,              javascriptChannels: Set.from([                JavascriptChannel(                    name: 'Print',                    onMessageReceived: (JavascriptMessage message) {                      //This is where you receive message from                       //javascript code and handle in Flutter/Dart                      //like here, the message is just being printed                      //in Run/LogCat window of android studio                      print(message.message);                    })              ]),              onWebViewCreated: (WebViewController w) {                webViewController = w;              },            )

and in Your HTMLfile:

<script type='text/javascript'>    Print.postMessage('Hello World being called from Javascript code');</script>

When you run this code, you shall be able to see log "Hello World being called from Javascript code" in the LogCat/Run window of android studio.


You can try my plugin flutter_inappbrowser (EDIT: it has been renamed to flutter_inappwebview) and use addJavaScriptHandler({@required String handlerName, @required JavaScriptHandlerCallback callback}) method (see more here).

An example is presented below.On Flutter side:

...child: InAppWebView(  initialFile: "assets/index.html",  initialHeaders: {},  initialOptions: InAppWebViewWidgetOptions(    inAppWebViewOptions: InAppWebViewOptions(        debuggingEnabled: true,    )  ),  onWebViewCreated: (InAppWebViewController controller) {    webView = controller;    controller.addJavaScriptHandler(handlerName: "mySum", callback: (args) {      // Here you receive all the arguments from the JavaScript side       // that is a List<dynamic>      print("From the JavaScript side:");      print(args);      return args.reduce((curr, next) => curr + next);    });  },  onLoadStart: (InAppWebViewController controller, String url) {  },  onLoadStop: (InAppWebViewController controller, String url) {  },  onConsoleMessage: (InAppWebViewController controller, ConsoleMessage consoleMessage) {    print("console message: ${consoleMessage.message}");  },),...

On JavaScript side (for example a local file assets/index.html inside the assets folder):

<!doctype html><html lang="en">    <head>        <meta charset="UTF-8">        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">        <meta http-equiv="X-UA-Compatible" content="ie=edge">        <title>Flutter InAppBrowser</title>        ...    </head>    <body>        ...        <script>           // In order to call window.flutter_inappwebview.callHandler(handlerName <String>, ...args)            // properly, you need to wait and listen the JavaScript event flutterInAppWebViewPlatformReady.            // This event will be dispatched as soon as the platform (Android or iOS) is ready to handle the callHandler method.            window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {             // call flutter handler with name 'mySum' and pass one or more arguments             window.flutter_inappwebview.callHandler('mySum', 12, 2, 50).then(function(result) {               // get result from Flutter side. It will be the number 64.               console.log(result);             });           });        </script>    </body></html>

On Android Studio logs you will get:

I/flutter (20436): From JavaScript side:I/flutter (20436): [12, 2, 50]I/flutter (20436): console message: 64


I want to tell you about how to send messages from flutter WebView to JS:

  1. In JS code you need to bind your function you need to fire to window
const function = () => alert('hello from JS');window.function = function;
  1. In your code in WebView widget implementation you need to declare onWebViewCreated method like this
WebView(  onWebViewCreated: (WebViewController controller) {},  initialUrl: 'https://url.com',  javascriptMode: JavascriptMode.unrestricted,)
  1. In class widget declare var _webViewController;
class App extends State<MyApp> {  final _webViewController;}
  1. In onWebViewCreated write this code
onWebViewCreated: (WebViewController controller) {    _webViewController = controller;},

Then you can run code like this:

class App extends StatelessWidget {  var _webViewController;  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Flutter Demo',      home: Scaffold(        body: WebView(          onWebViewCreated: (WebViewController controller) {            _webViewController = controller;          },          initialUrl: 'https://url.com',          javascriptMode: JavascriptMode.unrestricted,        ),        floatingActionButton: FloatingActionButton(          onPressed: () {            // When you click at this button youll run js code and youll see alert            _webViewController                .evaluateJavascript('window.function ()');          },          child: Icon(Icons.add),          backgroundColor: Colors.green,        ),      ),    );  }}

But what if we want to share this _webViewController instance to other widgets like drawer?
In this case I decided to implement Singleton pattern and store _webViewController instance in it.
So
Singleton class

class Singleton {  WebViewController webViewController;  static final Singleton _singleton = new Singleton._internal();  static Singleton get instance => _singleton;  factory Singleton(WebViewController webViewController) {    _singleton.webViewController = webViewController;    return _singleton;  }  Singleton._internal();}

Then

onWebViewCreated: (WebViewController controller) {  var singleton = new Singleton(controller);},

And finally in our Drawer widget i.e. (here you can use whatever widget you want)

class EndDrawer extends StatelessWidget {  final singleton = Singleton.instance;  @override  Widget build(BuildContext context) {    return Drawer(      child: Column(        mainAxisAlignment: MainAxisAlignment.end,        children: <Widget>[          SizedBox(              width: 200,              child: FlatButton(                onPressed: () {                  singleton.webViewController.evaluateJavascript('window.function()');                  Navigator.pop(context); // exit drawer                },                child: Row(                  children: <Widget>[                    Icon(                      Icons.exit_to_app,                      color: Colors.redAccent,                    ),                    SizedBox(                      width: 30,                    ),                    Text(                      'Exit',                      style: TextStyle(color: Colors.blueAccent, fontSize: 20),                    ),                  ],                ),              )),        ],      ),    );  }}

If you want to receive messages from JS code to your flutter App you need:

  1. In your js code
window.CHANNEL_NAME.postMessage('Hello from JS');
  1. In your flutter code.
    When you're running JavascriptChannel(name: 'CHANNEL_NAME', ...)
    flutter bind to your window WebView new MessageChannel with name you wrote in constructor (in this case CHANNEL_NAME)
    so when we call window.CHANNEL_NAME.postMessage('Hello from JS'); we recieve a message we sent
WebView(   javascriptChannels: [     JavascriptChannel(name: 'CHANNEL_NAME', onMessageReceived: (message) {       print(message.message);       })   ].toSet(),  initialUrl: 'https://url.com',)

So here we are.
I'm new in flutter code
So if you have another better experience about this you can write in comments to help other people!