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:
- In JS code you need to bind your function you need to fire to window
const function = () => alert('hello from JS');window.function = function;
- 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,)
- In class widget declare
var _webViewController;
class App extends State<MyApp> { final _webViewController;}
- 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:
- In your js code
window.CHANNEL_NAME.postMessage('Hello from JS');
- 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 caseCHANNEL_NAME
)
so when we callwindow.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!