flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge

flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge

在使用webview中,需要实现flutter与Javascript交互,在使用webview_flutter插件的时候,整理了一下webview与Javascript的交互JSBridge,具体可以查看

https://blog.csdn.net/gloryFlow/article/details/131683122

这里使用inappwebview插件来实现flutter与Javascript的交互JSBridge。

一、什么是JSBridge

JSBridge是一种实现webview与原生端的相互调用的能力。

在比较流行的JSBridge中,主要是通过拦截URL请求来达到 native 端和 webview 端相互通信的效果的。如WebviewJavascriptBridge。

那在inappwebview中有实现javascript交互的方式。在inappwebview中,可以使用JavaScript Handlers,来实现flutter端与javascript的交互。可以查看

https://blog.csdn.net/gloryFlow/article/details/133643136

在这里插入图片描述

二、修改JSBridge的JS端实现

在WebviewJavascriptBridge中,代码中使用iframe中,拦截url来达到webview与原生交互。那在inappwebview,我们可以直接嵌套使用JavaScript Handlers来实现交互。

我们定义WebviewJSBridgeReady

const String kWebviewJsBridgeReady = '''window.onerror = function(err) {log('window.onerror: ' + err)}function setupWebViewJavascriptBridge(callback) {if (window.AppJSBridge) {return callback(AppJSBridge);} else {document.addEventListener('AppJSBridgeReady', function() {callback(AppJSBridge);},false);}}setupWebViewJavascriptBridge(function(bridge) {bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {var responseData = { 'Javascript Says':'Right back atcha!' }responseCallback(responseData)});bridge.registerHandler('JSHandler', function(data, responseCallback) {var responseData = { 'Javascript Says':'Right back atcha!' }responseCallback(responseData)});}
''';

修改后的WebviewJsBridge,其中定义了sendMessageQueue、messageHandlers等。其中定义了一个window.AppJSBridge,创建了事件document.createEvent(‘Event’),初始化event.initEvent(‘AppJSBridgeReady’, true, true); 触发对象dispatch触发对象可以是任何元素或其他事件目标document.dispatchEvent(event);

const String kInAppWebViewJavascriptBridge = '''
function preprocessorJS() {if (window.AppJSBridge) {return;}if (!window.onerror) {window.onerror = function(msg, url, line) {console.log("AppJSBridge: ERROR:" + msg + "@" + url + ":" + line);}}// var messagingIframe;var sendMessageQueue = [];var messageHandlers = {};var responseCallbacks = {};var uniqueId = 1;var dispatchMessagesWithTimeoutSafety = true;function registerHandler(handlerName, handler) {messageHandlers[handlerName] = handler;}function callHandler(handlerName, data, responseCallback) {if (arguments.length == 2 && typeof data == 'function') {responseCallback = data;data = null;}_doSend({ handlerName:handlerName, data:data }, responseCallback);}function call(handlerName, data, responseCallback) {if (arguments.length == 2 && typeof data == 'function') {responseCallback = data;data = null;}_doSend({ handlerName:handlerName, data:data }, responseCallback);}function disableJavscriptAlertBoxSafetyTimeout() {dispatchMessagesWithTimeoutSafety = false;}function _doSend(message, responseCallback) {if (responseCallback) {var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();responseCallbacks[callbackId] = responseCallback;message['callbackId'] = callbackId;}sendMessageQueue.push(message);// 通过使用inappwebview的callHandlerwindow.flutter_inappwebview.callHandler(message['handlerName'], message);}function _fetchQueue() {var messageQueueString = JSON.stringify(sendMessageQueue);sendMessageQueue = [];return messageQueueString;}function _dispatchMessageFromObjC(messageJSON) {if (dispatchMessagesWithTimeoutSafety) {setTimeout(_doDispatchMessageFromObjC);} else {_doDispatchMessageFromObjC();}// 打印log_consoleLog("AppJSBridge: messageJSON:" + messageJSON);function _doDispatchMessageFromObjC() {var message = JSON.parse(messageJSON);var messageHandler;var responseCallback;if (message.responseId) {responseCallback = responseCallbacks[message.responseId];if (!responseCallback) {return;}responseCallback(message.responseData);delete responseCallbacks[message.responseId];} else {if (message.callbackId) {var callbackResponseId = message.callbackId;responseCallback = function(responseData) {_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });};}var handler = messageHandlers[message.handlerName];if (!handler) {_consoleLog("AppJSBridge: WARNING: no handler for message from ObjC:", message);} else {handler(message.data, responseCallback);}}}}function _handleMessageFromObjC(messageJSON) {_dispatchMessageFromObjC(messageJSON);}registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);window.AppJSBridge = {registerHandler: registerHandler,callHandler: callHandler,call: call,disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,_fetchQueue: _fetchQueue,_handleMessageFromObjC: _handleMessageFromObjC,_consoleLog: _consoleLog,};// 打印logfunction _consoleLog(message) {// 显示来自flutter的回调var logJSON = { 'message':message, 'logType':1 }callHandler("log", JSON.stringify(logJSON));}window.WeixinJSBridge = window.AppJSBridge;// 创建事件var event = document.createEvent('Event');// 定义事件名为'build'.event.initEvent('AppJSBridgeReady', true, true);event.bridge = window.AppJSBridge;// 触发对象可以是任何元素或其他事件目标document.dispatchEvent(event);
}function preprocessorReadyJs() {if (!window.flutter_inappwebview.callHandler) {window.flutter_inappwebview.callHandler = function () {var _callHandlerID = setTimeout(function () { });window.flutter_inappwebview._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));return new Promise(function (resolve, reject) {window.flutter_inappwebview[_callHandlerID] = resolve;});};}preprocessorJS();
}preprocessorReadyJs()''';

三、在flutter端使用InAppWebViewController实现调用方法的InAppWebJSHandlerManager

在flutter端使用InAppWebViewController实现调用方法,使用InAppWebViewController来调用evaluateJavascript来调用js的方法。我们可以在flutter端实现调用flutter_inappwebview插件的JavaScript Handlers。这里我定义了消息队列,定义了消息的JSMessage,与responseCallbacks回调。

// flutter_inappwebview插件的JavaScript Handlers
class InAppWebJSHandlerManager {
// flutter_inappwebview插件InAppWebViewController? inAppWebViewController;BuildContext? context;// 存储的消息messageHandlerMap<String, dynamic> messageHandlers = {};// 存储的回调callback, responseCallbackMap<String, dynamic> responseCallbacks = {};// 开启的消息队列,发送的消息均会存储到该队列中List<JSMessage>? startupMessageQueue = [];// 消息的标识int _uniqueId = 0;JSChannelManager() {// TODO: implement JSChannelManager}void updateController({required BuildContext context,InAppWebViewController? inAppWebViewController,}) {this.context = context;this.inAppWebViewController = inAppWebViewController;}// 处理消息队列void flutterFlushMessageQueue() async {// 获取h5发送的列表// 处理H5存的消息队列发送的MessageQueueString? messageQueueString;if (inAppWebViewController != null) {messageQueueString = await inAppWebViewController?.evaluateJavascript(source: webViewJavascriptFetchQueyCommand());}LoggerManager().debug("flutterFlushMessageQueue:${messageQueueString}");flushMessageQueue(messageQueueString);}// 处理来自H5的消息列表void flushMessageQueue(String? messageQueueString) {if (!(messageQueueString != null && messageQueueString.isNotEmpty)) {return;}dynamic? aFromH5Messages = jsonDecode(messageQueueString);if (aFromH5Messages != null && aFromH5Messages is List) {for (dynamic aMsgJson in aFromH5Messages) {if (aMsgJson is Map<String, dynamic>) {JSMessage jsMessage = JSMessage.fromJson(aMsgJson);LoggerManager().debug("flushMessageQueue aFromH5Messages aMsgJson:${aMsgJson} jsMessage:${jsMessage}");// 从H5获取或者接收到的消息,如果responseId不为空,则为flutter调用H5方法,H5给flutter的回调if (jsMessage.responseId != null &&jsMessage.responseId!.isNotEmpty) {// 如果responseId不为空,则为flutter调用H5方法,H5给flutter的回调ResponseCallback? responseCallback =responseCallbacks[jsMessage.responseId];if (responseCallback != null) {// 处理H5返回给flutter的回调responseCallback(jsMessage.responseData);}} else {ResponseCallback? responseCallback;// 如果responseId为空时候,则是来自H5发送的flutter的消息// 获取H5传过来的标识callbackIdString? callbackId = jsMessage.callbackId;if (callbackId != null && callbackId.isNotEmpty) {// 接收到来自H5的消息JSMessage aMessage = JSMessage();aMessage.copy(aNewMessage: aMessage, aOldMessage: jsMessage);responseCallback = (dynamic responseData) {// flutter回调给H5// 将H5传过来的callbackId作为responseId回调传递给H5aMessage.responseId = callbackId;aMessage.responseData = responseData;_queueMessage(aMessage);};} else {responseCallback = (dynamic responseData) {// callbackId为空,不做任何处理};}// 从flutter已经注册Register方法中找出对应的方法JSBridgeHandler? jsBridgeHandler =messageHandlers[jsMessage.handlerName];if (jsBridgeHandler != null) {// 在flutter该handlerName的方法已经注册registerjsBridgeHandler(jsMessage.data, responseCallback);} else {// 在flutter该handlerName没有注册,则不做任何处理}}}}}}// 处理从H5收到的消息void _dispatchMessage(JSMessage message) async {String messageJSON = jsonEncode(message.toJson());messageJSON = messageJSON.replaceAll("\\", "\\\\");messageJSON = messageJSON.replaceAll("\"", "\\\"");messageJSON = messageJSON.replaceAll("\'", "\\\'");messageJSON = messageJSON.replaceAll("\n", "\\n");messageJSON = messageJSON.replaceAll("\r", "\\r");messageJSON = messageJSON.replaceAll("\f", "\\f");messageJSON = messageJSON.replaceAll("\u2028", "\\u2028");messageJSON = messageJSON.replaceAll("\u2029", "\\u2029");String javascriptCommand =webViewJavascriptHandleMessageFromObjCCommand(messageJSON);if (inAppWebViewController != null) {await inAppWebViewController?.evaluateJavascript(source: javascriptCommand);}}// 注入jsvoid injectJavascript(String javascript) async {if (inAppWebViewController != null) {await inAppWebViewController?.evaluateJavascript(source: javascript);}}// 注入jsvoid injectJavascriptReady() async {if (inAppWebViewController != null) {await inAppWebViewController?.evaluateJavascript(source: '$kWebviewJsBridgeReady');}}// 注入jsvoid injectBridgeJavascript() async {if (inAppWebViewController != null) {await inAppWebViewController?.evaluateJavascript(source: '$kInAppWebViewJavascriptBridge');}LoggerManager().debug("injectJavascript");// 处理flutter发送的消息队列if (startupMessageQueue != null && startupMessageQueue!.isNotEmpty) {List<JSMessage> tmpList = startupMessageQueue!;startupMessageQueue = null;for (JSMessage message in tmpList) {_dispatchMessage(message);}}}// 向H5发送消息void _sendData(String handleName,{dynamic? data, ResponseCallback? responseCallback}) {String callbackId = "flutter_cb_${++_uniqueId}";JSMessage jsMessage = JSMessage();jsMessage.callbackId = callbackId;jsMessage.handlerName = handleName;jsMessage.data = data;// 将callbackId存储到responseCallbacks中,callbackId会被H5通过responseId返回if (responseCallback != null) {responseCallbacks[callbackId] = responseCallback;}_queueMessage(jsMessage);}// 将发送给H5的消息存到startupMessageQueue中void _queueMessage(JSMessage jsMessage) {if (startupMessageQueue != null) {startupMessageQueue!.add(jsMessage);}_dispatchMessage(jsMessage);}// 注入js的commandString webViewJavascriptCheckCommand() {return "typeof window.AppJSBridge == \'object\';";}String webViewJavascriptFetchQueyCommand() {return "AppJSBridge._fetchQueue();";}String webViewJavascriptHandleMessageFromObjCCommand(String messageJSON) {return "AppJSBridge._handleMessageFromObjC('${messageJSON}');";}// 判断AppJSBridgeFuture<String?> checkJavascriptBridge() async {String? result;if (inAppWebViewController != null) {bool jsBridge = await inAppWebViewController?.evaluateJavascript(source: webViewJavascriptCheckCommand());result = (jsBridge?"true":"false");}LoggerManager().debug("checkJavascriptBridge result:${result}");return result;}/// flutter开放出去的方法,flutter调用H5方法统一使用该callHandler/// callHandlervoid callHandler(String handleName,{dynamic? data, ResponseCallback? responseCallback}) {if (handleName.isNotEmpty) {_sendData(handleName, data: data, responseCallback: responseCallback);}}/// flutter注册方法/// flutter注册方法,提供给H5调用void registerHandler(String handlerName, JSBridgeHandler jsBridgeHandler) {if (handlerName.isNotEmpty) {messageHandlers[handlerName] = jsBridgeHandler;}}void addJSBridgeHandlers() {if (inAppWebViewController != null) {messageHandlers.forEach((handlerName, jsBridgeHandler) {inAppWebViewController?.addJavaScriptHandler(handlerName: handlerName, callback: (List<dynamic> arguments) {LoggerManager().debug("inAppWebViewController.addJavaScriptHandler arguments:${arguments}");flutterFlushMessageQueue();});});}}// 移除注册的方法void removeHandler(String handleName) {if (handleName.isNotEmpty) {messageHandlers.remove(handleName);}}// 重置,将responseCallbacks、startupMessageQueue重置void reset() {startupMessageQueue = [];responseCallbacks = {};_uniqueId = 0;}
}

InAppWebJSHandlerManager中使用inAppWebViewController?.addJavaScriptHandler来处理接收H5端的JS消息,并且将处理回调返回给H5。
当收到H5消息,flutter根据callbackId将处理后的结果回调给H5。

四、InAppWebJSBridgeRegister:appBridge调用的方法,flutter注册的方法

InAppWebJSBridgeRegister实现处理flutter注册的方法,提供相应的方法,H5端JS可以方法调用。

// appBridge调用的方法,flutter注册的方法
class InAppWebJSBridgeRegister {late InAppWebJSHandlerManager _inAppWebJSHandlerManager;// 支付final ChannelPayPlatform _channelPayPlatform = ChannelPayPlatform();// 打开app等final ChannelLauncher _channelLauncher = ChannelLauncher();// 弹窗final ChannelDialog _channelDialog = ChannelDialog();// 扫码或者识别二维码final ChannelQrScanner _channelQrScanner = ChannelQrScanner();InAppWebJSBridgeRegister({required InAppWebJSHandlerManager inAppWebJSHandlerManager}) {_inAppWebJSHandlerManager = inAppWebJSHandlerManager;}// 注册handlersvoid registerHandlers({JSChannelRegisterHandler? jsChannelRegisterHandler}) {// 设置标题_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.setTitle,(data, responseCallback) {if (data != null && data is String) {String title = data;if (jsChannelRegisterHandler != null) {jsChannelRegisterHandler(JSChannelRegisterMethod.setTitle, title);}}});// 获取用户昵称_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getUsername,(data, responseCallback) {UserModel userModel =Provider.of<UserModel>(OneContext().context!, listen: false);String userNickName = userModel.userNickName ?? "";if (responseCallback != null) {responseCallback(userNickName);}});// 获取定位_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getLoc,(data, responseCallback) {// TODO 获取定位});// 获取App名称_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAppName,(data, responseCallback) {PackageInfo.fromPlatform().then((packageInfo) {String appName = "${packageInfo.appName}";if (responseCallback != null) {responseCallback(appName);}});});// 获取版本号_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getVersion,(data, responseCallback) {PackageInfo.fromPlatform().then((packageInfo) {String version = "${packageInfo.buildNumber}";String versionCode = version.replaceAll(".", "");if (responseCallback != null) {responseCallback(versionCode);}});});// 获取用户id_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getUserId,(data, responseCallback) {UserModel userModel =Provider.of<UserModel>(OneContext().context!, listen: false);String userId = userModel.userId ?? "";if (responseCallback != null) {responseCallback(userId);}});// 获取用户登录认证token_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAuthorization,(data, responseCallback) {UserModel userModel =Provider.of<UserModel>(OneContext().context!, listen: false);String token = userModel.token ?? "";if (responseCallback != null) {responseCallback(token);}});// 调用支付(微信支付/支付宝支付)原生_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.setPayPlatform,(data, responseCallback) {_channelPayPlatform.openUniPay(data, responseCallback);});// 打开扫一扫_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.openScan,(data, responseCallback) {// 打开扫一扫界面_channelQrScanner.openScanner(JSChannelRegisterMethod.openScan, data, responseCallback);});// 打开扫一扫_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.scanQrCode,(data, responseCallback) {// 打开扫一扫界面_channelQrScanner.openScanner(JSChannelRegisterMethod.scanQrCode, data, responseCallback);});// 打系统电话_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.callTelPhone,(data, responseCallback) {_channelLauncher.openLauncher(JSChannelRegisterMethod.callTelPhone, data, responseCallback);});// 发送短信_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.sendSms,(data, responseCallback) {_channelLauncher.openLauncher(JSChannelRegisterMethod.sendSms, data, responseCallback);});// 对话框 showDialog_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.showDialog,(data, responseCallback) {_channelDialog.openShowDialog(data, responseCallback);});// 底部选择框_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.showCheckBox,(data, responseCallback) {_channelDialog.openShowSheetBox(data, responseCallback);});// 保存图片到相册_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.saveImage,(data, responseCallback) {// 保存图片到相册if (data != null && data is String && data.isNotEmpty) {FlutterLoadingHud.showLoading(message: "保存中...");SaveToAlbumUtil.saveImage(data, onCallback: (bool result, String message) {FlutterLoadingHud.dismiss();if (result) {// 保存成功FlutterLoadingHud.showToast(message: message);} else {// 保存失败FlutterLoadingHud.showToast(message: message);}});}});// 识别二维码_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.detectorQRCode,(data, responseCallback) {// 识别图片中的二维码_channelQrScanner.openScanner(JSChannelRegisterMethod.detectorQRCode, data, responseCallback);});// 打开App_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.openApp,(data, responseCallback) {_channelLauncher.openLauncher(JSChannelRegisterMethod.openApp, data, responseCallback);});// log_inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.log,(data, responseCallback) {Map<String, dynamic> dataJson = jsonDecode(data);int loggerType = dataJson["logType"];String message = dataJson["message"];if (LoggerMode.debug == loggerType) {LoggerManager().debug("registerHandlers log data: ${message}");} else if (LoggerMode.verbose == loggerType) {LoggerManager().verbose("registerHandlers log data: ${message}");} else if (LoggerMode.info == loggerType) {LoggerManager().info("registerHandlers log data: ${message}");} else if (LoggerMode.warning == loggerType) {LoggerManager().warning("registerHandlers log data: ${message}");} else if (LoggerMode.error == loggerType) {LoggerManager().error("registerHandlers log data: ${message}");}});_inAppWebJSHandlerManager.addJSBridgeHandlers();}// 处理是否跳转,true可跳转,false不可跳转bool shouldOverrideUrlLoading(Uri uri) {///在页面跳转之前调用 isForMainFrame为false,页面不跳转.导致网页内很多链接点击没效果String url = uri.toString();return webViewNavigationAllowed(url);}// 处理是否跳转,true可跳转,false不可跳转bool webViewNavigationAllowed(String url) {LoggerManager().debug('navigationDelegate decode $url');String telPrefix = "tel://";String smsPrefix = "sms://";String appPrefix = "app://";if (url.startsWith(telPrefix)) {String data = url.substring(telPrefix.length);_channelLauncher.openLauncher(JSChannelRegisterMethod.callTelPhone, data, null);// 不可跳转return false;}if (url.startsWith(smsPrefix)) {String data = url.substring(smsPrefix.length);_channelLauncher.openLauncher(JSChannelRegisterMethod.sendSms, data, null);// 不可跳转return false;}if (url.startsWith(appPrefix)) {// app://close_channelLauncher.openappUrl(url);return false;}if (url == "about:blank") {// 空页面进行跳转return true;}// 可跳转return true;}
}

五、定义JSMessage:H5与flutter交互的消息体

class JSMessage {// {handlerName: getSessionID, data: , callbackId: cb_2_1665631238605}// handlerNameString? handlerName;// data// flutter发送给H5的data,参数dynamic? data;/// callbackId,/// H5发送给flutter的callbackId,/// flutter处理后将调用 AppJSBridge._handleMessageFromObjC('%@');/// H5从responseCallbacks中根据callbackId找到callback回调方法进行执行String? callbackId;/// responseId/// flutter发送给H5的responseId,/// responseId和callbackId是一样的/// 如果是H5调用flutter时候,从H5过来的callbackId作为responseId回调给H5/// 如果是flutter调用H5,从flutter过来的callbackId作为responseId回调给flutterString? responseId;/// 回调的数据/// 如果是H5调用flutter时候,从flutter传给H5的responseData作为回调数据/// 如果是flutter调用H5,从H5传给flutter的responseData作为回调数据dynamic? responseData;JSMessage();JSMessage.fromJson(Map<String, dynamic> json) {callbackId = json['callbackId'];data = json['data'];handlerName = json['handlerName'];responseId = json['responseId'];responseData = json['responseData'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();data['callbackId'] = this.callbackId;data["data"] = this.data;data["handlerName"] = this.handlerName;data['responseId'] = this.responseId;data['responseData'] = this.responseData;return data;}void copy({required JSMessage aNewMessage, required JSMessage aOldMessage}) {aNewMessage.callbackId = aOldMessage.callbackId;aNewMessage.data = aOldMessage.data;aNewMessage.handlerName = aOldMessage.handlerName;aNewMessage.responseId = aOldMessage.responseId;aNewMessage.responseData = aOldMessage.responseData;}
}

六、封装inwebview中使用InAppWebJSBridgeRegister、InAppWebJSHandlerManager

我们封装inwebview中,使用InAppWebJSBridgeRegister、InAppWebJSHandlerManager。

定义InAppWebJSHandlerManager

// JS与Flutter调用的message Queuefinal InAppWebJSHandlerManager _inAppWebJSHandlerManager =InAppWebJSHandlerManager();

定义InAppWebJSBridgeRegister

// flutter注册供H5调用的方法late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;_inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(inAppWebJSHandlerManager: _inAppWebJSHandlerManager);

我们在onWebViewCreated中,可以得到InAppWebViewController,更新_inAppWebJSHandlerManager的controller

_inAppWebJSHandlerManager.updateController(context: context,inAppWebViewController: webViewController,);

在onWebViewCreated回调中注入ready代码

              // 注入jsReady_inAppWebJSHandlerManager.injectJavascriptReady();

在onWebViewCreated回调中注册flutter端的方法,

              // register a JavaScript handler with name "myHandlerName"_inAppWebJSBridgeRegister.registerHandlers(jsChannelRegisterHandler: (handlerName, data) {if (JSChannelRegisterMethod.setTitle == handlerName) {setWebPageTitle(data);}});

在onLoadStop回调中注入kInAppWebViewJavascriptBridge

            onLoadStop: (controller, url) async {// 注入_inAppWebJSHandlerManager.injectBridgeJavascript();_inAppWebJSHandlerManager.checkJavascriptBridge();// 加载完成widget.onLoadFinished(url.toString());},

完整代码如下

class WebViewInAppScreen extends StatefulWidget {const WebViewInAppScreen({Key? key,required this.url,this.onWebProgress,this.onWebResourceError,required this.onLoadFinished,required this.onWebTitleLoaded,this.onWebViewCreated,}) : super(key: key);final String url;final Function(int progress)? onWebProgress;final Function(String? errorMessage)? onWebResourceError;final Function(String? url) onLoadFinished;final Function(String? webTitle)? onWebTitleLoaded;final Function(InAppWebViewController controller)? onWebViewCreated;@overrideState<WebViewInAppScreen> createState() => _WebViewInAppScreenState();
}class _WebViewInAppScreenState extends State<WebViewInAppScreen> {final GlobalKey webViewKey = GlobalKey();InAppWebViewController? webViewController;InAppWebViewGroupOptions options = InAppWebViewGroupOptions(crossPlatform: InAppWebViewOptions(useShouldOverrideUrlLoading: true,mediaPlaybackRequiresUserGesture: false,applicationNameForUserAgent: "app-webview",),android: AndroidInAppWebViewOptions(useHybridComposition: true,),ios: IOSInAppWebViewOptions(allowsInlineMediaPlayback: true,),);// JS与Flutter调用的message Queuefinal InAppWebJSHandlerManager _inAppWebJSHandlerManager =InAppWebJSHandlerManager();// cookiefinal InAppWebJSCookieConfig _inAppWebViewJSCookieConfig =InAppWebJSCookieConfig();// flutter注册供H5调用的方法late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;bool _isDisposed = false;@overridevoid initState() {// TODO: implement initStatesuper.initState();_isDisposed = false;_inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(inAppWebJSHandlerManager: _inAppWebJSHandlerManager);}@overridevoid dispose() {// TODO: implement dispose_isDisposed = true;_inAppWebJSHandlerManager.reset();webViewController?.clearCache();// _inAppWebViewJSCookieConfig.clear();super.dispose();}// 设置页面标题void setWebPageTitle(data) {if (widget.onWebTitleLoaded != null) {widget.onWebTitleLoaded!(data);}}// flutter调用H5方法void callJSMethod() {_inAppWebJSHandlerManager.callHandler("JSAPPHandler",data: {"id": "a18c9fe0d"}, responseCallback: (dynamic responseData) {LoggerManager().debug("callJSMethod responseData:${responseData}");FlutterLoadingHud.showToast(message: jsonEncode(responseData));});}@overrideWidget build(BuildContext context) {return Column(children: <Widget>[Expanded(child: InAppWebView(key: webViewKey,initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),initialUserScripts: UnmodifiableListView<UserScript>([UserScript(source:"document.cookie='token=${ApiAuth.getToken()};domain='.inice.cn';path=/'",injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START),]),initialOptions: options,onWebViewCreated: (controller) {webViewController = controller;_inAppWebViewJSCookieConfig.setCookie(widget.url);_inAppWebJSHandlerManager.updateController(context: context,inAppWebViewController: webViewController,);// 注入jsReady_inAppWebJSHandlerManager.injectJavascriptReady();// register a JavaScript handler with name "myHandlerName"_inAppWebJSBridgeRegister.registerHandlers(jsChannelRegisterHandler: (handlerName, data) {if (JSChannelRegisterMethod.setTitle == handlerName) {setWebPageTitle(data);}});String filePre = "file://";if (widget.url.startsWith(filePre)) {String html = widget.url.substring(filePre.length);webViewController?.loadFile(assetFilePath: 'assets/htmls/${html}');} else {if (widget.url.startsWith("http://") ||widget.url.startsWith("https://")) {webViewController?.loadUrl(urlRequest: URLRequest(url: Uri.parse(widget.url)));}}if (widget.onWebViewCreated != null) {widget.onWebViewCreated!(controller);}},onTitleChanged: (controller, title) {if (widget.onWebTitleLoaded != null) {widget.onWebTitleLoaded!(title);}},onLoadStart: (controller, url) {},androidOnPermissionRequest: (controller, origin, resources) async {return PermissionRequestResponse(resources: resources,action: PermissionRequestResponseAction.GRANT);},shouldOverrideUrlLoading: (controller, navigationAction) async {var uri = navigationAction.request.url!;bool canNavigate =_inAppWebJSBridgeRegister.shouldOverrideUrlLoading(uri);// 允许路由替换return canNavigate? NavigationActionPolicy.ALLOW: NavigationActionPolicy.CANCEL;},onLoadStop: (controller, url) async {// 注入_inAppWebJSHandlerManager.injectBridgeJavascript();_inAppWebJSHandlerManager.checkJavascriptBridge();// 加载完成widget.onLoadFinished(url.toString());},onProgressChanged: (controller, progress) {if (widget.onWebProgress != null) {widget.onWebProgress!(progress);}},onLoadError: (controller, Uri? url, int code, String message) {if (widget.onWebResourceError != null) {widget.onWebResourceError!(message);}},onUpdateVisitedHistory: (controller, url, androidIsReload) {print("onUpdateVisitedHistory:${url}");},onConsoleMessage: (controller, consoleMessage) {print("consoleMessage:${consoleMessage}");},),),Container(height: ScreenUtil().bottomBarHeight + 50.0,color: Colors.white,child: Column(children: [Expanded(child: Row(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[ElevatedButton(child: Icon(Icons.arrow_back),onPressed: () {webViewController?.goBack();},),SizedBox(width: 25.0,),ElevatedButton(child: Icon(Icons.arrow_forward),onPressed: () {webViewController?.goForward();},),SizedBox(width: 25.0,),ElevatedButton(child: Icon(Icons.refresh),onPressed: () {// callJSMethod();webViewController?.reload();},),],),),Container(height: ScreenUtil().bottomBarHeight,),],),),],);}
}

七、使用inappwebview的page

最后,我们使用inappwebview,使用一个页面打开对应的需要的链接地址,这里使用的本地测试页面

class InAppWebViewPage extends StatefulWidget {const InAppWebViewPage({Key? key, this.arguments}) : super(key: key);final Object? arguments;@overrideState<InAppWebViewPage> createState() => _InAppWebViewPageState();
}class _InAppWebViewPageState extends State<InAppWebViewPage> {String title = "";String? url;double webProgress = 0.0;@overridevoid initState() {// TODO: implement initStateif (widget.arguments != null && widget.arguments is Map) {Map obj = widget.arguments as Map;url = obj["url"];}LoggerManager().debug("_WebViewPageState arguments:${widget.arguments}");LoggerManager().debug("_WebViewPageState url:${url}");super.initState();}@overridevoid dispose() {// TODO: implement disposesuper.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(resizeToAvoidBottomInset: false,appBar: WebAppBar(toolbarHeight: 44.0,backgroundColor: Theme.of(context).primaryColor,centerWidget: Text("${title}",textAlign: TextAlign.center,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 17,color: ColorUtil.hexColor(0xffffff),fontWeight: FontWeight.w600,fontStyle: FontStyle.normal,decoration: TextDecoration.none,),),leadingWidget: Row(mainAxisAlignment: MainAxisAlignment.start,crossAxisAlignment: CrossAxisAlignment.center,children: [IconButton(padding: EdgeInsets.all(0.0),onPressed: () {navigatorBack();},icon: Icon(Icons.close_rounded,color: Colors.white,size: 30.0,),),],),trailingWidget: Row(mainAxisAlignment: MainAxisAlignment.end,crossAxisAlignment: CrossAxisAlignment.center,children: [SizedBox(width: 28.0,),SizedBox(width: 28.0,),],),),body: Stack(children: [WebViewInAppScreen(url: url ?? "",onWebProgress: (int progress) {if (mounted) {// TODO onWebProgressdouble precent = progress / 100.0;if (precent > 1.0) {precent = 1.0;}if (precent < 0.0) {precent = 0.0;}setState(() {webProgress = precent;LoggerManager().debug("webProgress:${webProgress}");});}},onLoadFinished: (String? url) {if (mounted) {// TODO onLoadFinished}},onWebTitleLoaded: (String? webTitle) {if (mounted) {setState(() {title = webTitle ?? "";});}},),buildProgressIndicator(context),],),);}Widget buildProgressIndicator(BuildContext context) {return (webProgress != 1.0)? LinearProgressIndicator(backgroundColor: Colors.transparent,valueColor: AlwaysStoppedAnimation(ColorUtil.hexColor(0x3b93ff)),value: webProgress,minHeight: 2,): Container();}void navigatorBack() {NavigatorPageRouter.pop();}
}

到此,inappwebview实现flutter与Javascript的交互JSBridge基本内容已经完成了。
查看效果
在这里插入图片描述
在这里插入图片描述

八、小结

inappwebview实现flutter与Javascript的交互JSBridge。描述可能不是特别准确,请见谅。

https://blog.csdn.net/gloryFlow/article/details/133667017

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/99017.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

九大高效的前端测试工具与框架

前言&#xff1a; 在每个Web应用程序中&#xff0c;作为用户直接可见的应用程序外观&#xff0c;“前端”包括&#xff1a;图形化的用户界面、相应的功能、及其整体站点的可用性。我们可以毫不夸张地说&#xff1a;如果前端无法正常工作&#xff0c;您将无法“拉新”网站的潜在…

端粒/端粒酶生信切入点,6+端粒酶+泛癌+甲基化+实验。

今天给同学们分享一篇端粒酶泛癌甲基化实验的生信文章“Genomic, epigenomic, and transcriptomic signatures for telomerase complex components: a pan‐cancer analysis”&#xff0c;这篇文章于2022年10月31日发表在Mol Oncol期刊上&#xff0c;影响因子为6.6。 激活端粒酶…

拍摄的照片怎么做二维码?一分钟在线生成二维码

​手机拍摄的照片怎么做成二维码呢&#xff1f;用二维码来查看图片的方式现在很多人都在使用&#xff0c;其优点在于不占用自身空间&#xff0c;还可以拥有更快速度让他人查看图片内容&#xff0c;常见的图片二维码类型一般有信息展示、照片展示、商品海报、表情包等等。图片二…

伦敦银最新走势不利怎么办

跟其他的投资品种一样&#xff0c;伦敦银的价格走势在不停的变化&#xff0c;而且由于本身产品具有较高的资金杠杆&#xff0c;所以万一行情走势变得不利&#xff0c;在很短的时间之内就会对投资者的账户造成严重损失&#xff0c;所以投资者应该对此作好充分的准备。 伦敦银的最…

TypeScript 笔记:String 字符串

1 对象属性 length 返回字符串的长度 2 对象方法 charAt() 返回在指定位置的字符 charCodeAt() 返回在指定的位置的字符的 Unicode 编码 concat 连接两个或更多的字符串 indexOf 返回某个指定的字符串值在字符串中首次出现的位置 lastIndexOf 从后向前搜索字符串&…

计算机网络(四):网络层

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 网络层概述 网络层的主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输 要实现网络层任务&#xff0c;需要解决以下主要问题 网络层向运输层提供怎样的服务 (“…

QQ浏览器怎么才能设置默认搜索引擎为百度

问题&#xff1a; 打开QQ浏览器&#xff0c;搜索相关信息时发现总是默认为”搜狗搜索引擎“&#xff0c;想将其转为”百度搜索引擎“ 解决&#xff1a; 1、点击浏览器右侧”菜单“图标&#xff0c;选择”设置“&#xff0c;如下图所示&#xff1a; 2、在”常规设置“中的”搜…

竞赛选题 深度学习 YOLO 实现车牌识别算法

文章目录 0 前言1 课题介绍2 算法简介2.1网络架构 3 数据准备4 模型训练5 实现效果5.1 图片识别效果5.2视频识别效果 6 部分关键代码7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 该项目较…

二叉搜索树的基础操作

如果对于二叉搜索树不是太清楚&#xff0c;为什么要使用二叉搜索树&#xff1f;作者推荐&#xff1a;二叉搜索树的初步认识_加瓦不加班的博客-CSDN博客 定义节点 static class BSTNode {int key; // 若希望任意类型作为 key, 则后续可以将其设计为 Comparable 接口Object val…

3.(vue3.x+vite)class动态绑定的方式

前端技术社区总目录(订阅之前请先查看该博客) 效果浏览 代码如下 <template><div><div :class="{acti

通用监控视频web播放方案

业务场景 对接监控视频&#xff0c;实现海康大华等监控摄像头的实时画面在web端播放 方案一&#xff0c;使用 RTSP2webnode.jsffmpeg 说明&#xff1a;需要node环境&#xff0c;原理就是RTSP2web实时调用ffmpeg解码。使用单独html页面部署到服务器后&#xff0c;在项目中需要播…

IP 子网划分(VLSM)

目录 一、 为什么要划分子网 二、如何划分子网 1、划分两个子网 2、划分多个子网 一、 为什么要划分子网 假设有一个B类IP地址172.16.0.0&#xff0c;B类IP的默认子网掩码是 255.255.0.0&#xff0c;那么该网段内IP的变化范围为 172.16.0.0 ~ 172.16.255.255&#xff0c;即…

【Java 进阶篇】HTML块级元素详解

HTML&#xff08;Hypertext Markup Language&#xff09;是用于创建网页的标记语言。在HTML中&#xff0c;元素被分为块级元素和内联元素两种主要类型。块级元素通常用于构建网页的结构&#xff0c;而内联元素则嵌套在块级元素内&#xff0c;用于添加文本和其他内容。本文将重点…

C++设计模式-单件(Singleton)

目录 C设计模式-单件&#xff08;Singleton&#xff09; 一、意图 二、适用性 三、结构 四、参与者 五、代码 C设计模式-单件&#xff08;Singleton&#xff09; 一、意图 保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。 二、适用性 当类只能有一…

【17】c++设计模式——>原型模式

原型模式的定义 c中的原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;其目的是通过复制&#xff08;克隆&#xff09;已有对象来创建新的对象&#xff0c;而不需要显示的使用构造函数创建对象&#xff0c;原型模式适用于创建复杂对象时&a…

浅谈内存函数以及模拟实现

1.memcpy void * memcpy ( void * destination, const void * source, size_t num ); 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。 这个函数在遇到 \0 的时候并不会停下来。 如果source和destination有任何的重叠&#xff0c;复制的结果都…

保姆级微服务部署教程

大家好&#xff0c;我是鱼皮。 项目上线是每位学编程同学必须掌握的基本技能。之前我已经给大家分享过很多种上线单体项目的方法了&#xff0c;今天再出一期微服务项目的部署教程&#xff0c;用一种最简单的方法&#xff0c;带大家轻松部署微服务项目。 开始之前&#xff0c;…

Linux虚拟机搭建RabbitMQ集群

普通集群模式&#xff0c;意思就是在多台机器上启动多个 RabbitMQ 实例&#xff0c;每台机器启动一个。创建的 queue&#xff0c;只会放在一个 RabbitMQ 实例上&#xff0c;但是每个实例都同步 queue 的元数据&#xff08;元数据可以认为是 queue 的一些配置信息&#xff0c;通…

自定义无边框窗口

效果&#xff1a; 可拖动拉伸 ui&#xff1a;设计如下 样式表&#xff1a;在ui CustomDialog 里设置的 #widget_title{background: #E6F1EB;border-top-left-radius: 20px;border-top-right-radius: 20px;}#widget_client{background-color: rgb(255, 255, 255);border-bottom…

react create-react-app v5配置 px2rem (不暴露 eject方式)

环境信息&#xff1a; create-react-app v5 “react”: “^18.2.0” “postcss-plugin-px2rem”: “^0.8.1” 配置步骤&#xff1a; 不暴露 eject 配置自己的webpack&#xff1a; 1.下载react-app-rewired 和 customize-cra-5 npm install react-app-rewired customize-cra…