In our daily development projects, it is inevitable to use the H5 page for the rapid development and iteration of the project. When developing a project with Flutter, there is also a need to load the H5 page. Opening the H5 page in mobile development requires the Use of the WebView component. At the same time, in order to exchange data with H5 pages, it is sometimes necessary to use JSBridge to realize the communication between the client and H5.

Add the webview_FLUTTER component

Add the dependency webview_flutter: ^3.0.0 to the project’s pubspec.yaml file and then perform pub get.

  • Since loading a WebView requires networking, you also need to add network permissions to Android. Open directory android/app/SRC/main/AndroidManifest. XML, then add the following code.
<uses-permission android:name="android.permission.INTERNET"/>
Copy the code
  • Since iOS has Https enabled by default in version 9.0, to run Http pages, you also need to add the following code to the iOS /Runner/ info.plist file.
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
Copy the code

A brief introduction to the construction of the webview_FLUTTER component

WebView({Key Key, this.onWebViewCreated, // this. InitialUrl, JavascriptMode = javascriptMode. Disabled, // javascript execution mode, // Javascript can call this. NavigationDelegate, // Route delegate, You can use this.gesturerecognizers in your app to stop the page in your app. // / / page loading complete callback enclosing onWebResourceError, / / resource loading failure callback enclosing debuggingEnabled = false, enclosing gestureNavigationEnabled = false, this.userAgent, this.initialMediaPlaybackPolicy = AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, })Copy the code

When loading a web page using a Webview, many times you need to interact with JS, that is, JS calls Flutter and Flutter calls JS.

Check the official documentation to find the capabilities provided, which you can refer to in actual development. Include: network request, Cookies related, cache related, load HTML, load failed pages and so on.

Future<void> _onShowUserAgent( WebViewController controller, BuildContext context) async { // Send a message with the user agent string to the Toaster JavaScript channel we registered // with the WebView. await controller.runJavascript( 'Toaster.postMessage("User Agent: " + navigator.userAgent); '); } Future<void> _onListCookies( WebViewController controller, BuildContext context) async { final String cookies = await controller.runJavascriptReturningResult('document.cookie'); // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: <Widget>[ const Text('Cookies:'), _getCookieList(cookies), ], ), )); } Future<void> _onAddToCache( WebViewController controller, BuildContext context) async { await controller.runJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry"; '); // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); } Future<void> _onListCache( WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Toaster.postMessage(caches))'); } Future<void> _onClearCache( WebViewController controller, BuildContext context) async { await controller.clearCache();  // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(const SnackBar( content: Text('Cache cleared.'), )); } Future<void> _onClearCookies(BuildContext context) async { final bool hadCookies = await cookieManager.clearCookies();  String message = 'There were cookies. Now, they are gone! '; if (! hadCookies) { message = 'There are no cookies.'; } // ignore: deprecated_member_use Scaffold.of(context).showSnackBar(SnackBar( content: Text(message), )); } Future<void> _onNavigationDelegateExample( WebViewController controller, BuildContext context) async { final String contentBase64 = base64Encode(const Utf8Encoder().convert(kNavigationExamplePage)); await controller.loadUrl('data:text/html; base64,$contentBase64'); } Future<void> _onSetCookie( WebViewController controller, BuildContext context) async { await CookieManager().setCookie( const WebViewCookie( name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'), ); await controller.loadUrl('https://httpbin.org/anything'); } Future<void> _onDoPostRequest( WebViewController controller, BuildContext context) async { final WebViewRequest request = WebViewRequest( uri: Uri.parse('https://httpbin.org/post'), method: WebViewRequestMethod.post, headers: <String, String>{'foo': 'bar', 'Content-Type': 'text/plain'}, body: Uint8List.fromList('Test Body'.codeUnits), ); await controller.loadRequest(request); } Future<void> _onLoadLocalFileExample( WebViewController controller, BuildContext context) async { final String pathToIndex = await _prepareLocalFile(); await controller.loadFile(pathToIndex); } Future<void> _onLoadFlutterAssetExample( WebViewController controller, BuildContext context) async { await controller.loadFlutterAsset('assets/www/index.html'); } Future<void> _onLoadHtmlStringExample( WebViewController controller, BuildContext context) async { await controller.loadHtmlString(kLocalExamplePage); } Future<void> _onTransparentBackground( WebViewController controller, BuildContext context) async { await controller.loadHtmlString(kTransparentBackgroundPage); } Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); } final List<String> cookieList = cookies.split('; '); final Iterable<Text> cookieWidgets = cookieList.map((String cookie) => Text(cookie)); return Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: cookieWidgets.toList(), ); } static Future<String> _prepareLocalFile() async { final String tmpDir = (await getTemporaryDirectory()).path; final File indexFile = File( <String>{tmpDir, 'www', 'index.html'}.join(Platform.pathSeparator)); await indexFile.create(recursive: true); await indexFile.writeAsString(kLocalExamplePage); return indexFile.path; }Copy the code

JS calls Flutter

NavigationDelegate method implementation

This way to achieve the principle of the main load of web pages for interception.

Here is an example of the code implemented

Js code:

document.location = "js://webview? name=candy";Copy the code

Flutter end code:

navigationDelegate: (NavigationRequest request) {if(request.url.startswith ("js://webview")) {print(" start processing ${request.url}"); return NavigationDecision.prevent; } return NavigationDecision.navigate; },Copy the code

NavigationDecision here. Prevent said stop routing to replace NavigationDecision. Navigate said allow routing to replace.

javascriptChannels

Js code:

<button onclick="callFlutter()">callFlutter</button>
function callFlutter(){
   Toast.postMessage("js call flutter");  
}
Copy the code

Flutter end code:

WebView( javascriptChannels: <JavascriptChannel>[ _shareJavascriptChannel(context), ].toSet(), ) JavascriptChannel _shareJavascriptChannel(BuildContext context) { return JavascriptChannel( name: 'share', onMessageReceived: (JavascriptMessage message) {print(" parameter: ${message.message}"); showToast(message.message); }); }Copy the code

Flutter call JS

Js code:

function callJS(message){
document.getElementById("p1").style.visibility = message;
}
Copy the code

Flutter code

Future<void> evaluateJavascript() async { print('evaluateJavascript'); _controller.runJavascript('callJS('visible'); '); }Copy the code

Loading local HTML

The HTML code

<! DOCTYPE html> <html> <body> <style>*{font-size:50px; }</style> <button onclick="callFlutter()">callFlutter</button> <p id="p1" style="visibility:hidden;" > Flutter code calls the JS method. < / p > < script SRC = "http://code.jquery.com/jquery-2.1.4.min.js" > < / script > < script SRC = "http://cdn.amazeui.org/amazeui/2.5.0/js/amazeui.min.js" > < / script > < script type = "text/javascript" > function callJS(message){ document.getElementById("p1").style.visibility = message; } </script> <script type="text/javascript"> function callFlutter(){ Toaster.postMessage('js call flutter'); } </script> </body> </html>Copy the code
Future<void> _loadHtmlFromAsset() async {
  String html = 'assets/static/test.html';
  final String path = await rootBundle.loadString(html);
  _controller.loadUrl(Uri.dataFromString(path,
      mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
      .toString());
}
Copy the code

Complete implementation code

import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:webview_flutter/webview_flutter.dart'; class WebViewPage extends StatefulWidget { @override _WebViewPageState createState() => _WebViewPageState(); } class _WebViewPageState extends State<WebViewPage> { late WebViewController _controller; String _title = "webview"; Void > _loadHtmlFromAsset() async {String Html = 'assets/static/test.html'; final String path = await rootBundle.loadString(html); _controller.loadUrl(Uri.dataFromString(path, mimeType: 'text/html', encoding: Encoding.getByName('utf-8')) .toString()); } @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text("$_title"), ), child: SafeArea( child: WebView( //initialUrl: "Https://flutterchina.club/", / / JS execution mode whether to allow JS perform javascriptMode: javascriptMode. Unrestricted, onWebViewCreated: (controller) { _controller = controller; _loadHtmlFromAsset(); }, onPageFinished: (url) async {/ / call JS method, obtained the title of the page String title = await _controller. RunJavascriptReturningResult (' document. The title '); setState(() { _title = title; }); evaluateJavascript(); }, navigationDelegate: (NavigationRequest request) {if(request.url.startswith ("js://webview")) {print(" start processing ${request.url}"); return NavigationDecision.prevent; } return NavigationDecision.navigate; }, javascriptChannels: <JavascriptChannel>{ JavascriptChannel( name: "share", onMessageReceived: (JavascriptMessage message) {print(" parameter: ${message.message}"); Callbackname = message. Message; callbackName = message. String data = "Received message called "; String script = "$callbackname($data)"; _controller.runJavascript(script); },},),),); } Future<void> evaluateJavascript() async { print('evaluateJavascript'); _controller.runjavascript ('callJS('visible'); '); }}Copy the code

Page loading

Assign a value to this property

initialUrl: 'https://flutterchina.club/',
Copy the code

Whether the page can be rolled back

Future<bool> _goBack(BuildContext context) async { if (_controller ! = null && await _controller.canGoBack()) { _controller.goBack(); return false; } return true; }Copy the code

Pub.dev /packages/we…

The above explains webView’s web page loading, JS interaction, network request, Cookies related, cache related, loading HTML, loading failed pages and other functions.