As a follow up to my explaining how to create communication bridges in Android and iOS, I thought it might be a good idea to do the same for Flutter. While it may seem like this is a straightforward affair, you’ll soon realize it takes a bit of work to get this functionality working. article First and foremost, it is important to realize that (at the time of writing this article) Flutter does have built in support for embedded WebViews. Meaning, that unlike a native application in either Kotlin or Swift, where you can just instantiate a WebView component, you cannot just add a WebView component to your application out of the box. NOT After creating a new Flutter project, we need to use the to be able to use a WebView. We will add the dependency to our pubspec.yaml file: webview_flutter package dependencies: flutter: sdk: flutter webview_flutter: ^1.0.7 Then, we need to run or in the terminal: Pub get flutter pub get Next, we need to import the package in our main.dart file: ; import 'package:webview_flutter/webview_flutter.dart' If you haven’t cleaned up the code from the starter project yet, now is a good time to do so. After you remove all the comments, the floating action button and everything related to it, you will be left with this (I added a text widget just for show): ; ; ; main() { runApp(MyApp()); } { Widget build(BuildContext context) { MaterialApp( title: , theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: ), ); } } { MyHomePage({Key key, .title}) : (key: key); title; _MyHomePageState createState() => _MyHomePageState(); } { WebViewController _controller; Widget build(BuildContext context) { Text( ); } } import 'dart:convert' import 'package:flutter/material.dart' import 'package:webview_flutter/webview_flutter.dart' void class MyApp extends StatelessWidget @override return 'Communication Bridge' 'Native - JS Communication Bridge' class MyHomePage extends StatefulWidget this super final String @override < > class _MyHomePageState extends State MyHomePage @override return "Flutter JS-Native Communication Bridge" Which will give you this result: Adding Our Local File Since we will be using a local html file with embedded JavaScript code inside of it, we need to create it in our project. All local assets in a Flutter application need to reside inside an directory. Create an assets directory in your main project hierarchy by right clicking in the left side panel and choosing New → Directory. This directory needs to be a sibling of the android directory. assets Then, go on ahead and create inside the assets directory. index.html My Local HTML File Hello World! < > html < > head < > title </ > title </ > head < > body < = > h1 id "title" </ > h1 < = > script type "text/javascript" { .getElementById( ).innerHTML = newTitle; sendBack(); } { messageHandler.postMessage( ); } ( ) function fromFlutter newTitle document "title" ( ) function sendBack "Hello from JS" </ > script </ > body </ > html You will notice that we have written two methods in the JavaScript section of our html: - is the method we will call from Flutter with a string representing the new title for the page fromFlutter - is the method we will call to communicate back to Flutter. In it we are sending a string message. sendBack We will get into the contents of sendBack in a minute, but before that, we have to set up our WebView in our application. ✋ Don’t forget to add index.html to your pubspec.yaml under an assets section (use the correct indentation) dependencies: flutter: sdk: flutter webview_flutter: ^1.0.7 cupertino_icons: ^1.0.0 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true assets: - assets/index.html Setting Up The WebView Since we already imported the package into our main.dart file, we need to replace the Text widget with a WebView widget. { WebViewController _controller; Widget build(BuildContext context) { Scaffold( appBar: AppBar(title: Text( )), body: WebView( initialUrl: , onWebViewCreated: (WebViewController webviewController) { _controller = webviewController; _loadHtmlFromAssets(); }, ), ); } _loadHtmlFromAssets() { file = rootBundle.loadString( ); _controller.loadUrl( .dataFromString( file, mimeType: , encoding: Encoding.getByName( )).toString()); } } < > class _MyHomePageState extends State MyHomePage @override return 'Webview' 'about:blank' async String await 'assets/index.html' Uri 'text/html' 'utf-8' We wrapped our WebView with a Scaffold widget(it’s purpose will be revealed later in the article), but let’s focus on the different fields of the WebView widget seen above: - is where we can define which url the WebView points to. Here we decided to point it to nothing since we are going to load our local html file initialUrl - is a callback we get from the package once the WebView is created. Since we want to save the controller instance that we get from this callback, we have created a private member to store it to( ) onWebViewCreated _controller You will also notice that we created a method called , which as is implied by it’s name, will load our local html file into the WebView. _loadHtmlFromAssets Inside this method, we use our private WebViewController instance, _controller, and it’s exposed method to load our local html file. Due to the logic in this method, it’s execution is asynchronous. loadUrl If we run our application, we will get the following: Let’s Communicate (Flutter -> WebView) Now let’s add some functionality to call the method we defined in our local html file. To do that, we will be adding a Floating Action Button (or FAB) to our layout and connecting it’s onPressed method to call the method. That is also the reason behind the usage of the Scaffold widget, so we can easily add a FAB. fromFlutter fromFlutter Widget build(BuildContext context) { Scaffold( appBar: AppBar(title: Text( )), body: WebView( initialUrl: , javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController webviewController) { _controller = webviewController; _loadHtmlFromAssets(); }, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.arrow_upward), onPressed: () { _controller.evaluateJavascript( ); }, ), ); } @override return 'Webview' 'about:blank' const 'fromFlutter("From Flutter")' In order to make calls from Flutter to our loaded html we are using the method. To be able to use it, we must add another property to our WebView, called . Above, we are setting it to unrestricted. If we don’t set it, we will not be able to communicate between Flutter and the WebView. evaluateJavascript javascriptMode Communicate Back (WebView -> Flutter) Remember how I said we will talk about the contents of our sendBack method? Well, now is the time we do that. { messageHandler.postMessage( ); } ( ) function sendBack "Hello from JS" In the sendBack method we are using an object called and it’s attached method called . If you have ever created a communication bridge in a native application, then you are aware that once you set one up, you are adding an object to the global window object in the Javascript layer to be used for communication. You can name this object to whatever you like as long as you reference it when you make calls from Javascript to your native application. messageHandler postMessage How is this object added to the Javascript layer in our application? By adding the attribute to our WebView widget: JavascriptChannels { WebViewController _controller; GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); Widget build(BuildContext context) { Scaffold( key: _scaffoldKey, appBar: AppBar(title: Text( )), body: WebView( initialUrl: , javascriptMode: JavascriptMode.unrestricted, javascriptChannels: .from([ JavascriptChannel( name: , onMessageReceived: (JavascriptMessage message) { _scaffoldKey.currentState.showSnackBar( SnackBar( content: Text(message) ) ); }) ]), onWebViewCreated: (WebViewController webviewController) { _controller = webviewController; _loadHtmlFromAssets(); }, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.arrow_upward), onPressed: () { _controller.evaluateJavascript( ); }, ), ); } < > class _MyHomePageState extends State MyHomePage final new @override return 'Webview' 'about:blank' Set 'messageHandler' const 'fromFlutter("From Flutter")' We have defined a JavascriptChannel with a name and an onMessageReceived handler. The name we have given this channel, , is the name we are using to communicate from the local html file we loaded to our native layer. messageHandler For the keen eyed, you probably noticed that a new private variable has been added, . This is because we needed to add a key to our Scaffold widget so we can display the Snackbar. _scaffoldKey You can get the source code for the application described in this article . here Two final points to be aware of: The alert method is broken in the webview_flutter package To use the package in iOS, you must add the following key to your info.plist file: io.flutter.embedded_views_preview yes < > key </ > key < > string </ > string Other sources you may find helpful if you want to learn more about Flutter and WebViews: The Power Of WebViews In Flutter WebView_Flutter Package Also published at https://medium.com/flutter-community/js-native-communication-bridge-in-flutter-f94b65913df1