項目背景
隨著項目的不斷完善奋蔚,需要做一些運營活動饼齿,通常是H5頁面屡萤,在app中打開這些H5頁面就需要引入WebView锈死,有時候H5需要客戶端提供一些內部的接口贫堰,這時候我們就需要建立JSBridge用于客戶端與H5之間的通訊。
使用
1待牵、安裝webview_flutter
webview_flutter: ^0.3.16
- 在android中添加網(wǎng)絡權限其屏,目錄android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
3、 ios中在ios/Runner/Info.plist中添加
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>*
4缨该、建立JSBridge
- 需要與H5開發(fā)約定協(xié)議偎行,建立Model
/// WebView與h5進行通信
class JsBridge {
String method; // 方法名
Map data; // 傳遞數(shù)據(jù)
Function success; // 執(zhí)行成功回調
Function error; // 執(zhí)行失敗回調
JsBridge(this.method, this.data, this.success, this.error);
/// jsonEncode方法中會調用實體類的這個方法。如果實體類中沒有這個方法,會報錯蛤袒。
Map toJson() {
Map map = new Map();
map["method"] = this.method;
map["data"] = this.data;
map["success"] = this.success;
map["error"] = this.error;
return map;
}
/// jsonDecode(jsonStr)方法返回的是Map<String, dynamic>類型熄云,需要這里將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}";
}
}
- 對接收到的h5方法,進行內部處理汗盘,這里舉個簡單的例子皱碘,客戶端向h5提供了打開微信App的接口openWeChatApp
import 'dart:convert';
import 'package:fluwx/fluwx.dart' as fluwx;
import '../models/JsBridge.dart';
import '../utils/toast.dart';
class JsBridgeUtil {
/// 將json字符串轉化成對象
static JsBridge parseJson(String jsonStr) {
JsBridge jsBridgeModel = JsBridge.fromMap(jsonDecode(jsonStr));
return jsBridgeModel;
}
/// 向H5開發(fā)接口調用
static executeMethod(context, JsBridge jsBridge) async{
if (jsBridge.method == 'openWeChatApp') {
/// 先檢測是否已安裝微信
bool _isWechatInstalled = await fluwx.isWeChatInstalled();
if (!_isWechatInstalled) {
toast.show(context, '您沒有安裝微信');
jsBridge.error?.call();
return;
}
fluwx.openWeChatApp();
jsBridge.success?.call();
}
}
}
- 建立可復用的Webview組件
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../configs/config.dart';
import '../utils/adapter.dart';
import '../utils/privacyProtocolDialog.dart';
import '../utils/JsBridgeUtil.dart';
import '../widgets/AppIcon.dart';
import '../models/appState.dart';
import '../models/userState.dart';
/// WebView頁面
class Webview extends StatefulWidget {
final String url;
final String title;
WebViewController webViewController; // 添加一個controller
final PrivacyProtocolDialog privacyProtocolDialog;
Webview({Key key, this.url, this.title = '', this.privacyProtocolDialog})
: super(key: key);
@override
WebViewState createState() => WebViewState();
}
class WebViewState extends State<Webview> {
bool isPhone = Adapter.isPhone();
JavascriptChannel _JsBridge(BuildContext context) => JavascriptChannel(
name: 'FoxApp', // 與h5 端的一致 不然收不到消息
onMessageReceived: (JavascriptMessage msg) async{
String jsonStr = msg.message;
JsBridgeUtil.executeMethod(JsBridgeUtil.parseJson(jsonStr));
});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: isPhone ? Colors.white : Color(Config.foxColors.bg),
appBar: AppBar(
backgroundColor: isPhone ? null : Color(Config.foxColors.bg),
leading: AppIcon(Config.foxImages.backGreyUrl,
callback: (){
Navigator.of(context).pop(true);
if (widget.privacyProtocolDialog != null) { // 解決切換頁面時彈框顯示異常問題
privacyProtocolDialog.show(context);
}
}),
title: Text(widget.title),
centerTitle: true,
elevation: 0,
),
body: StoreConnector<AppState, UserState>(
converter: (store) => store.state.userState,
builder: (context, userState) {
return WebView(
initialUrl: widget.url,
userAgent:"Mozilla/5.0 foxApp", // h5 可以通過navigator.userAgent判斷當前環(huán)境
javascriptMode: JavascriptMode.unrestricted, // 啟用 js交互,默認不啟用JavascriptMode.disabled
javascriptChannels: <JavascriptChannel>[
_JsBridge(context) // 與h5 通信
].toSet(),
);
}),
);
}
}
- h5頁面中引入隐孽,并調用JsBridge
<!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>Document</title>
</head>
<script src="https://cdn.bootcss.com/jquery/2.0.1/jquery.js"></script>
<body>
coming baby!
<script>
$('body').html('<p>hello world</p>');
FoxApp.postMessage(JSON.stringify({method:"openWeChatApp"}));
</script>
</body>
</html>
- flutter 調用h5方法
// flutter
webViewController.evaluateJavascript("sayHello('hello world')")
// h5
function sayHello(msg){
console.log(msg)
}
以上實現(xiàn)了一個簡易版的JsBridge,webview_flutter是官網(wǎng)插件健蕊,功能非常強大菱阵,JS與Flutter交互只是常用的功能,還提供了頁面加載缩功、路由晴及、手勢等功能,感興趣的同學可以去更詳細的了解嫡锌。