文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-10-12 12:07

关注

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,    };        // 打印log  function _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 Handlersclass InAppWebJSHandlerManager {// flutter_inappwebview插件  InAppWebViewController? inAppWebViewController;  BuildContext? context;  // 存储的消息messageHandler  Map messageHandlers = {};  // 存储的回调callback, responseCallback  Map responseCallbacks = {};  // 开启的消息队列,发送的消息均会存储到该队列中  List? 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存的消息队列发送的MessageQueue    String? 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) {          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传过来的标识callbackId            String? 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回调传递给H5                aMessage.responseId = callbackId;                aMessage.responseData = responseData;                _queueMessage(aMessage);              };            } else {              responseCallback = (dynamic responseData) {                // callbackId为空,不做任何处理              };            }            // 从flutter已经注册Register方法中找出对应的方法            JSBridgeHandler? jsBridgeHandler =            messageHandlers[jsMessage.handlerName];            if (jsBridgeHandler != null) {              // 在flutter该handlerName的方法已经注册register              jsBridgeHandler(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);    }  }  // 注入js  void injectJavascript(String javascript) async {    if (inAppWebViewController != null) {      await inAppWebViewController?.evaluateJavascript(source: javascript);    }  }  // 注入js  void injectJavascriptReady() async {    if (inAppWebViewController != null) {      await inAppWebViewController?.evaluateJavascript(source: '$kWebviewJsBridgeReady');    }  }  // 注入js  void injectBridgeJavascript() async {    if (inAppWebViewController != null) {      await inAppWebViewController?.evaluateJavascript(source: '$kInAppWebViewJavascriptBridge');    }    LoggerManager().debug("injectJavascript");    // 处理flutter发送的消息队列    if (startupMessageQueue != null && startupMessageQueue!.isNotEmpty) {      List 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的command  String webViewJavascriptCheckCommand() {    return "typeof window.AppJSBridge == \'object\';";  }  String webViewJavascriptFetchQueyCommand() {    return "AppJSBridge._fetchQueue();";  }  String webViewJavascriptHandleMessageFromObjCCommand(String messageJSON) {    return "AppJSBridge._handleMessageFromObjC('${messageJSON}');";  }  // 判断AppJSBridge  Future 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  /// callHandler  void 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 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;  }  // 注册handlers  void 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(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(OneContext().context!, listen: false);          String userId = userModel.userId ?? "";          if (responseCallback != null) {            responseCallback(userId);          }        });    // 获取用户登录认证token    _inAppWebJSHandlerManager.registerHandler(JSChannelRegisterMethod.getAuthorization,            (data, responseCallback) {          UserModel userModel =          Provider.of(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 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}  // handlerName  String? 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回调给flutter  String? responseId;  /// 回调的数据  /// 如果是H5调用flutter时候,从flutter传给H5的responseData作为回调数据  /// 如果是flutter调用H5,从H5传给flutter的responseData作为回调数据  dynamic? responseData;  JSMessage();  JSMessage.fromJson(Map json) {    callbackId = json['callbackId'];    data = json['data'];    handlerName = json['handlerName'];    responseId = json['responseId'];    responseData = json['responseData'];  }  Map toJson() {    final Map data = new Map();    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 Queue  final 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;  @override  State createState() => _WebViewInAppScreenState();}class _WebViewInAppScreenState extends State {  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 Queue  final InAppWebJSHandlerManager _inAppWebJSHandlerManager =      InAppWebJSHandlerManager();  // cookie  final InAppWebJSCookieConfig _inAppWebViewJSCookieConfig =      InAppWebJSCookieConfig();  // flutter注册供H5调用的方法  late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;  bool _isDisposed = false;  @override  void initState() {    // TODO: implement initState    super.initState();    _isDisposed = false;    _inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(        inAppWebJSHandlerManager: _inAppWebJSHandlerManager);  }  @override  void 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));    });  }  @override  Widget build(BuildContext context) {    return Column(      children: [        Expanded(          child: InAppWebView(            key: webViewKey,            initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),            initialUserScripts: UnmodifiableListView([              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: [                    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;  @override  State createState() => _InAppWebViewPageState();}class _InAppWebViewPageState extends State {  String title = "";  String? url;  double webProgress = 0.0;  @override  void initState() {    // TODO: implement initState    if (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();  }  @override  void dispose() {    // TODO: implement dispose    super.dispose();  }  @override  Widget 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 onWebProgress                double 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

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

来源地址:https://blog.csdn.net/gloryFlow/article/details/133667017

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯