Flutter development practice-inappwebview implements flutter and Javascript method calls

Flutter development practice-inappwebview implements flutter and Javascript method calls

When using inappwebview, the flutter side needs to interact with JS and call the corresponding methods, JavaScript Handlers in inappwebview.

1. JavaScript Handlers

To add JavaScript Handlers, you can use the InAppWebViewController.addJavaScriptHandler method, where you define the handlerName and the callback to be called when it is called from the JavaScript side. The callback can return data to be sent on the JavaScript side. If you need to manage JavaScript handlers immediately after the web page is loaded, you should call InAppWebViewController.addJavaScriptHandler when creating the InAppWebView.

Here is an example of how to register a JavaScript handler:

onWebViewCreated: (controller) {
  // register a JavaScript handler with name "myHandlerName"
  controller.addJavaScriptHandler(handlerName: 'myHandlerName', callback: (args) {
    // print arguments coming from the JavaScript side!
    print(args);

    // return data to the JavaScript side!
    return {
      'bar': 'bar_value', 'baz': 'baz_value'
    };
  });
},

On the JavaScript side, to execute the callback handler and send data to Flutter, you need to use the window.Flutter_inappwebview.callHandler(handlerName, …args) method, where handlerName is a string representing the name of the handler you are calling and args are Optional parameters that can be sent to the Fluter side.

Notice:

If you want to change the name, we can change the name to nest window.flutter_inappwebview

window.myCustomObj = { callHandler: window.flutter_inappwebview.callHandler } and, then, you can use window.myCustomObj.callHandler

Additionally, the entire specific processing code can be wrapped this way:

const myHandlerName = (…args) => window.flutter_inappwebview.callHandler(myHandlerName’, …args);

Then call myHandlerName();

On the Javascript side, if you need to call callHandler, you need to listen to flatterInAppWebViewPlatformReady. You can use the global flag variable set when the fllatterInAppWebViewPlatformReady event is dispatched and use it before calling the window.flutter_inappwebview.callHandler method.

The sample code is as follows

// execute inside the "flutterInAppWebViewPlatformReady" event listener
window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
 const args = [1, true, ['bar', 5], {foo: 'baz'}];
 window.flutter_inappwebview.callHandler('myHandlerName', ...args);
});

// or using a global flag variable
var isFlutterInAppWebViewReady = false;
window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
 isFlutterInAppWebViewReady = true;
});
// then, somewhere in your code
if (isFlutterInAppWebViewReady) {
 const args = [1, true, ['bar', 5], {foo: 'baz'}];
 window.flutter_inappwebview.callHandler('myHandlerName', ...args);
}

On the flutter side, when Flutter executes the injection method, it calls evaluateJavascript to execute the callHandler. The flutterInAppWebViewPlatformReady does not need to be monitored because the flutterInAppWebViewPlatformReady is already Ready.

Code can be called in onLoadStop

onLoadStop: (controller, url) async {
  await controller.evaluateJavascript(source: """
    const args = [1, true, ['bar', 5], {foo: 'baz'}];
    window.flutter_inappwebview.callHandler('myHandlerName', ...args);
  """);
},

window.flutter_inappwebview.callHandler returns a JavaScript Promise that can be used to obtain the json result returned by the callback. In this case just return the data you want to send and it will be automatically json encoded using jsonEncode from the dart:convert library.

A simple sample code

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();

  if (Platform.isAndroid) {
    await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
  }
  
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {

  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
      android: AndroidInAppWebViewOptions(
        useHybridComposition: true,
      ),);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home:Scaffold(
          appBar: AppBar(title: Text("JavaScript Handlers")),
          body: SafeArea(
              child: Column(children: <Widget>[
                Expanded(
                  child: InAppWebView(
                    initialData: InAppWebViewInitialData(
                        data: """
<!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">
    </head>
    <body>
        <h1>JavaScript Handlers</h1>
        <script>
            window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
                window.flutter_inappwebview.callHandler('handlerFoo')
                  .then(function(result) {
                    // print to the console the data coming
                    // from the Flutter side.
                    console.log(JSON.stringify(result));
                    
                    window.flutter_inappwebview
                      .callHandler('handlerFooWithArgs', 1, true, ['bar', 5], {foo: 'baz'}, result);
                });
            });
        </script>
    </body>
</html>
                      """
                    ),
                    initialOptions: options,
                    onWebViewCreated: (controller) {
                      controller.addJavaScriptHandler(handlerName: 'handlerFoo', callback: (args) {
                        // return data to the JavaScript side!
                        return {
                          'bar': 'bar_value', 'baz': 'baz_value'
                        };
                      });

                      controller.addJavaScriptHandler(handlerName: 'handlerFooWithArgs', callback: (args) {
                        print(args);
                        // it will print: [1, true, [bar, 5], {foo: baz}, {bar: bar_value, baz: baz_value}]
                      });
                    },
                    onConsoleMessage: (controller, consoleMessage) {
                      print(consoleMessage);
                      // it will print: {message: {"bar":"bar_value","baz":"baz_value"}, messageLevel: 1}
                    },
                  ),
                ),
              ]))),
    );
  }
}


2. Listening to custom CustomEvent

It is possible to set a message event listener (for use with postMessage) or a custom event listener.

// message event listener
window.addEventListener("message", (event) => {
  console.log(event.data);
}, false);

// or custom event listener
window.addEventListener("myCustomEvent", (event) => {
  console.log(event.detail);
}, false);

Then use window.dispatch

// using postMessage method
window.postMessage({foo: 1, bar: false});

// or dispatching a custom event
const event = new CustomEvent("myCustomEvent", {
    detail: {foo: 1, bar: false}
});
window.dispatchEvent(event);

So, these event listeners can be set at runtime using the InAppWebViewController.eevaluatteJavascript method or inside the web application and dispatch these events using the same method.

For example:

onLoadStop: (controller, url) async {
  await controller.evaluateJavascript(source: """
    window.addEventListener("myCustomEvent", (event) => {
      console.log(JSON.stringify(event.detail));
    }, false);
  """);

  await Future.delayed(Duration(seconds: 5));

  controller.evaluateJavascript(source: """
    const event = new CustomEvent("myCustomEvent", {
      detail: {foo: 1, bar: false}
    });
    window.dispatchEvent(event);
  """);
},
onConsoleMessage: (controller, consoleMessage) {
  print(consoleMessage);
  // it will print: {message: {"foo":1,"bar":false}, messageLevel: 1}
},

3. Summary

Flutter development practice-inappwebview implements flutter and Javascript method calls. The description may not be particularly accurate, please forgive me.

Study and record, keep improving every day.