项目背景
随着项目的不断完善,需要做一些运营活动,通常是H5页面,在app中打开这些H5页面就需要引入WebView,有时候H5需要客户端提供一些内部的接口,这时候我们就需要建立JSBridge用于客户端与H5之间的通讯。
使用
1、安装webview_flutter
webview_flutter: ^0.3.16
- 在android中添加网络权限,目录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开发约定协议,建立Model
/// WebView与h5进行通信
class JsBridge {
String method; // 方法名
Map data; // 传递数据
Function success; // 执行成功回调
Function error; // 执行失败回调
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开发接口调用
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判断当前环境
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)
}
以上实现了一个简易版的JsBridge,webview_flutter是官网插件,功能非常强大,JS与Flutter交互只是常用的功能,还提供了页面加载、路由、手势等功能,感兴趣的同学可以去更详细的了解。