• 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)
            });
        }
    ''';
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    修改后的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的callHandler
    		window.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()
    
    ''';
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157

    三、在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;
    
      // 存储的消息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;
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255

    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;
      }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246

    五、定义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;
      }
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    六、封装inwebview中使用InAppWebJSBridgeRegister、InAppWebJSHandlerManager

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

    定义InAppWebJSHandlerManager

    // JS与Flutter调用的message Queue
      final InAppWebJSHandlerManager _inAppWebJSHandlerManager =
          InAppWebJSHandlerManager();
    
    
    • 1
    • 2
    • 3
    • 4

    定义InAppWebJSBridgeRegister

    // flutter注册供H5调用的方法
      late InAppWebJSBridgeRegister _inAppWebJSBridgeRegister;
    
    _inAppWebJSBridgeRegister = InAppWebJSBridgeRegister(
            inAppWebJSHandlerManager: _inAppWebJSHandlerManager);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    _inAppWebJSHandlerManager.updateController(
                    context: context,
                    inAppWebViewController: webViewController,
                  );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在onWebViewCreated回调中注入ready代码

                  // 注入jsReady
                  _inAppWebJSHandlerManager.injectJavascriptReady();
    
    
    • 1
    • 2
    • 3

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

                  // register a JavaScript handler with name "myHandlerName"
                  _inAppWebJSBridgeRegister.registerHandlers(
                      jsChannelRegisterHandler: (handlerName, data) {
                    if (JSChannelRegisterMethod.setTitle == handlerName) {
                      setWebPageTitle(data);
                    }
                  });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在onLoadStop回调中注入kInAppWebViewJavascriptBridge

                onLoadStop: (controller, url) async {
                  // 注入
                  _inAppWebJSHandlerManager.injectBridgeJavascript();
    
                  _inAppWebJSHandlerManager.checkJavascriptBridge();
    
                  // 加载完成
                  widget.onLoadFinished(url.toString());
                },
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    完整代码如下

    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,
                  ),
                ],
              ),
            ),
          ],
        );
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235

    七、使用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();
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

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

    八、小结

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

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

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

  • 相关阅读:
    【父母必知】呼吸过敏知多少
    民安智库(第三方市场调研公司)保障性住房满意度调查流程
    B+ 树的简单认识
    【触想智能】工业级触摸显示器的分类与应用分享
    【MySQL】 B+ 树存储的原理
    PostgreSQL基础入门
    【0144】postmaster创建System V Shared Memory(共享内存)的背后机制
    GPT-4o
    基于Open3D和PyTorch3D读取三维数据格式OBJ
    纯CSS 毛玻璃效果
  • 原文地址:https://blog.csdn.net/gloryFlow/article/details/133667017