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 }
});
相关推荐
牧羊狼的狼16 小时前
React 中的 HOC 和 Hooks
前端·javascript·react.js·hooks·高阶组件·hoc
知识分享小能手18 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
魔云连洲18 小时前
深入解析:Vue与React的异步批处理更新机制
前端·vue.js·react.js
mCell18 小时前
JavaScript 的多线程能力:Worker
前端·javascript·浏览器
超级无敌攻城狮20 小时前
3 分钟学会!波浪文字动画超详细教程,从 0 到 1 实现「思考中 / 加载中」高级效果
前端
excel20 小时前
用 TensorFlow.js Node 实现猫图像识别(教学版逐步分解)
前端
gnip21 小时前
JavaScript事件流
前端·javascript
赵得C21 小时前
【前端技巧】Element Table 列标题如何优雅添加 Tooltip 提示?
前端·elementui·vue·table组件
wow_DG21 小时前
【Vue2 ✨】Vue2 入门之旅 · 进阶篇(一):响应式原理
前端·javascript·vue.js
weixin_4569042721 小时前
UserManagement.vue和Profile.vue详细解释
前端·javascript·vue.js