Flutter支持webview并实现双向通信

在 flutter 项目开发中,避免不了在 app 中会使用一些 H5页面,所以需要在 app 中嵌入 webview;

安装依赖

首先要在项目中安装 webview_flutter 插件,我这里安装的版本是:

yaml 复制代码
webview_flutter: ^4.0.2

添加 Bridge

集成 webview 可以查看官方文档案例(此处就不单独再说明)。 创建一个 WebViewController,注意这个对象每个 webview 窗口是独立的,打开了两个网页就是两个 controller,所以数据不会互通,它可以控制 webview 的所有细节,例如导航、通信等等。

dart 复制代码
// 这里是创建controller的简单例子,具体详细参数查看官方文档
final controller =  WebViewController.fromPlatformCreationParams(params);

通过给 controller 添加 JavaScriptChannel 来处理消息和发送消息,该方法为 webview 中打开的网页添加一个全局对象(这个名称是可以自定义的),例如下面的代码:

dart 复制代码
controller.addJavaScriptChannel(
	// 会在H5的window上挂载一个FlutterBridge对象,该对象包含一个postMessage方法发送数据给flutter
	"FlutterBridge",
	onMessageReceived: (JavaScriptMessage message) {  
	  // 处理H5发来的消息以及数据响应  
	  // message 仅仅包含一个字符串类型的字段,就是 message.message
	}
);

我们可以在 onMessageReceived 中去解析 message,并做出一些响应,但是目前来看是单向的数据流通,如果我们想在 flutter 处理消息之后给网页一个结果,我们就要做一些其他的处理了;

消息处理

首先我们顶一个通用的消息 JSON 结构,当然这个结构按照自己的需求和喜好去自定义即可,以下面为例子:

dart 复制代码
import 'dart:convert';

class JsMessageData {
  const JsMessageData(this.type, this.params, this.callback);
  // 事件类型,一般这个是方法的名称
  final String type;
  // 参数
  final Map<String, dynamic> params;
  // 回调函数名,如果网页需要返回结果就需要传递这个函数
  final String callback;
  // fromJson, 输入是string
  factory JsMessageData.fromJson(String jsonStr) {
    final json = jsonDecode(jsonStr);
    return JsMessageData(
      json["type"],
      json["params"] ?? {},
      json["callback"] ?? "",
    );
  }
}

我们在下面尝试来实现 onMessageReceived 方法,来处理一下消息;

dart 复制代码
// 这里的逻辑可以抽取到单独的模块中进行维护
FutureOr hello(name: string) {
	return { "hello": name };
}
final onMessageReceived = (JavaScriptMessage message) async {
	// 这里解析出来真实的消息对象
	final data = JsMessageData.fromJson(message.message);
	// 获取js传入的回调函数名称,这里很像jsonp的实现
	final callbackName = data.callback;
	// 定义返回的结果
	dynamic result = {};
	// 执行flutter中定义的方法
	if (data.type == 'hello') {
		result = await hello(data.params["name"]);
	}
	// 判断是否有回调函数,有的话就回调结果给网页调用者
	if (callbackName.isNotEmpty) {
		// 最终还是通过JSON的方式返回给H5
		controller.runJavaScript("$callbackName(${jsonEncode(result)})");
	}
}

H5中调用

接下来我们在 H5中尝试调用,因为 flutter 接受到的消息是字符串类型,所以我们将要传输的数据转换成 JSON 字符串后再传递给 flutter,然后在 flutter 端再解析出来。

typescript 复制代码
interface IMessage {  
  type: string;  // 消息类型
  params?: Record<string, any>;  
}
// 定义Bridge
class Bridge {
	async sendWithResult(message: IMessage): Promise<any> {  
	  return new Promise(resolve => {  
	    // 随机生成一个函数名称  
	    const callbackName = `callback_${Math.floor(  
	      Math.random() * 100000  
	    )}`;  
	    // 将函数挂载到window上  
	    window[callbackName] = (data: any) => {  
	      // 执行回调  
	      resolve(data);  
	      // 执行完毕后删除  
	      delete window[callbackName];  
	    };  
	    window.FlutterBridge?.postMessage(  
	      JSON.stringify({  
	        ...message,  
	        callback: `window.${callbackName}`  
	      })  
	    );  
	  });  
	}
}

// 业务中使用
const bridge = new Bridge();
bridge.sendWithResult({
	type: 'hello',
	params: {
		name: 'zhangsan'
	}
}).then(resp => {
	// resp => { "hello": name }
});
相关推荐
m0_74825093几秒前
html 通用错误页面
前端·html
来吧~9 分钟前
vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)
前端·vue.js·音视频
江上清风山间明月15 分钟前
flutter bottomSheet 控件详解
android·flutter·底部导航·bottomsheet
鎈卟誃筅甡22 分钟前
Vuex 的使用和原理详解
前端·javascript
呆呆小雅27 分钟前
二、创建第一个VUE项目
前端·javascript·vue.js
m0_7482393333 分钟前
前端(Ajax)
前端·javascript·ajax
Fighting_p37 分钟前
【记录】列表自动滚动轮播功能实现
前端·javascript·vue.js
前端Hardy39 分钟前
HTML&CSS:超炫丝滑的卡片水波纹效果
前端·javascript·css·3d·html
技术思考者42 分钟前
HTML速查
前端·css·html
缺少动力的火车43 分钟前
Java前端基础—HTML
java·前端·html