Flutter uses JsBridge to deal with the communication between Webview and H5

  • 2021-11-30 01:38:41
  • OfStack

At present, mobile cross-platform development, as an important part of mobile development, is a skill that mobile developers must master and an important means of self-improvement. As a cross-platform technical solution launched by Google, Flutter has many advantages, and has been or is being applied by developers in mobile application development. In the past 2019, I saw more and more companies and individuals start to use Flutter to develop cross-platform applications. For mobile application development, Flutter can meet almost all business development needs, so it is time to learn Flutter.

As we all know, when using Flutter for project development, it is inevitable to load H5 pages, and opening H5 pages in mobile development requires WebView components. 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. In addition, the Hybrid development model also requires frequent interaction between Webview and JS.

Installation

This article uses the Flutter official webview_flutter component, the latest version of which is 0.3. 19 +9. You need to add the webview_flutter plug-in dependency before using it, as shown below.


webview_flutter: 0.3.19+9

Then, use the flutter packages get command to pull the plug-in locally and keep the dependency. Since loading the WebView requires the use of a network, you also need to add network permissions to the android. Open the directory android/app/src/main/AndroidManifest. xml and add the following code.


<uses-permission android:name="android.permission.INTERNET"/>

Since Https is turned on by default in version 9.0 of iOS, to run the Http web page, 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>

Basic use

Open the source code of the WebView component, and the constructor of the WebView component is as follows.


const WebView({
 Key key,
 this.onWebViewCreated,
 this.initialUrl,
 this.javascriptMode = JavascriptMode.disabled,
 this.javascriptChannels,
 this.navigationDelegate,
 this.gestureRecognizers,
 this.onPageStarted,
 this.onPageFinished,
 this.debuggingEnabled = false,
 this.gestureNavigationEnabled = false,
 this.userAgent,
 this.initialMediaPlaybackPolicy =
  AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
 }) : assert(javascriptMode != null),
  assert(initialMediaPlaybackPolicy != null),
  super(key: key);

Among them, the more common attributes have the following meanings:

onWebViewCreated: Called after WebView is created and will only be called once; initialUrl: url of the original load; javascriptMode: JS execution mode (whether JS is allowed to execute); javascriptChannels: Channel for JS and Flutter communication; navigationDelegate: Routing delegate (JS invokes Flutter section by intercepting url here); gestureRecognizers: Gesture monitoring; onPageFinished: Callback when WebView has finished loading. import 'dart: async';

When loading web pages using Webview, you often need to interact with JS, that is, JS calls Flutter and Flutter calls JS. Flutter Calling JS is simple, simply calling the _ controller. evaluateJavascript () function. While JS calls Flutter is more annoying, because javascriptChannels directory only supports string type, and JS method is fixed, that is, only postMessage method can be used, which is no problem for iOS, but there is a problem for Android. Of course, it can also be realized by modifying the source code.

JS calls Flutter

javascriptChannels mode

javascriptChannels mode is also recommended, which is mainly used for JS to Flutter data transfer. For example, there is the following JS code.


<button onclick="callFlutter()">callFlutter</button>
function callFlutter(){
 Toast.postMessage("JS Called Flutter"); 
}

Use postMessage method Toast is a well-defined name, when accepting to take this name to receive, the code of Flutter is as follows.


WebView(
  javascriptChannels: <JavascriptChannel>[ 
  _alertJavascriptChannel(context),
 ].toSet(),
)

JavascriptChannel _alertJavascriptChannel(BuildContext context) {
 return JavascriptChannel(
  name: 'Toast',
  onMessageReceived: (JavascriptMessage message) {
  showToast(message.message);
  });
}

navigationDelegate

In addition, another way is navigationDelegate, which is mainly intercepted when loading web pages, such as the following JS protocol.


document.location = "js://webview?arg1=111&args2=222";

The corresponding Flutter code is as follows.


navigationDelegate: (NavigationRequest request) {
 if (request.url.startsWith('js://webview')) { 
 showToast('JS Called Flutter By navigationDelegate'); 
 print('blocking navigation to $request}');
 Navigator.push(context,
  new MaterialPageRoute(builder: (context) => new testNav()));
 return NavigationDecision.prevent;
 }
 print('allowing navigation to $request');
 return NavigationDecision.navigate; // There must be 
},

Where NavigationDecision. prevent denotes blocking route replacement and NavigationDecision. navigate denotes allowing route replacement.

JSBridge

In addition, we can also develop JSBridge ourselves and establish a set of general specifications. First, you need to develop a contract agreement with H5 to establish Model.


class JsBridge {
 String method; //  Method name 
 Map data; //  Transfer data 
 Function success; //  Execute a successful callback 
 Function error; //  Execute a failed callback 

 JsBridge(this.method, this.data, this.success, this.error);

 /// jsonEncode This method of the entity class is called in the. If this method is not in the entity class, an error will be reported. 
 Map toJson() {
 Map map = new Map();
 map["method"] = this.method;
 map["data"] = this.data;
 map["success"] = this.success;
 map["error"] = this.error;
 return map;
 }
 
 static JsBridge fromMap(Map<String, dynamic> map) {
 JsBridge jsonModel = new JsBridge(map['method'], map['data'], map['success'], map['error']);
 return jsonModel;
 }

 @override
 String toString() {
 return "JsBridge: {method: $method, data: $data, success: $success, error: $error}";
 }
}

Then, the received H5 method is processed internally. For example, the client provides H5 with the interface openWeChatApp to open WeChat App, as shown below.


class JsBridgeUtil {
 ///  Will json String to object 
 static JsBridge parseJson(String jsonStr) {
 JsBridge jsBridgeModel = JsBridge.fromMap(jsonDecode(jsonStr));
 return jsBridgeModel;
 }

 ///  Toward H5 Development interface call 
 static executeMethod(context, JsBridge jsBridge) async{
 if (jsBridge.method == 'openWeChatApp') {
  ///  First check whether WeChat is installed 
  bool _isWechatInstalled = await fluwx.isWeChatInstalled();
  if (!_isWechatInstalled) {
  toast.show(context, ' You don't have WeChat installed ');
  jsBridge.error?.call();
  return;
  }
  fluwx.openWeChatApp();
  jsBridge.success?.call();
 }
 }
}

To make our encapsulated WebView more versatile, we can encapsulate the Webview as shown below.


<uses-permission android:name="android.permission.INTERNET"/>
0

When JS needs to call Flutter, just call JsBridge directly, as shown below.


<uses-permission android:name="android.permission.INTERNET"/>
1

Related articles: