粮草机器人 原生 Flutter 嵌入 WebView 场景
在 Flutter 开发中,我们经常需要嵌入 HTML 页面(比如通过 WebView 或 Flutter Web 内嵌 HTML 元素)
实现混合开发,这就离不开 HTML(JavaScript)和 Dart 之间的双向桥接通信。不同的运行场景下,实现桥
接的方式也有区别,下面分两种常见场景给出具体实现方案。
📱 原生 Flutter 嵌入 WebView 场景
这是最常见的场景:App 端用 webview_flutter 打开 H5 页面,需要实现 JS 和 Dart 的双向调用,这里给大
家整理完整可运行的实现流程。
1. 基础配置
首先在项目 pubspec.yaml 中引入依赖:
yaml
dependencies:
flutter:
sdk: flutter
webview_flutter:^4.4.0
安装依赖后,分别给不同平台添加配置:
Android:在 android/app/src/main/AndroidManifest.xml 添加网络访问权限
iOS:在 ios/Runner/Info.plist 添加 io.flutter.embedded_views_preview 权限,开启 WebView 视图支持
2. 完整双向通信实现
① Dart 接收 JS 发送的消息
通过 JavaScriptChannel 注册通道,JS 只需要调用 postMessage 就能把数据发送给 Dart:
dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class JsBridgeWebView extends StatefulWidget {
const JsBridgeWebView({super.key});
@override
State<JsBridgeWebView> createState() => _JsBridgeWebViewState();
}
class _JsBridgeWebViewState extends State<JsBridgeWebView> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_initWebView();
}
void _initWebView() {
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
// 注册接收JS消息的通道
..addJavaScriptChannel(
'FlutterBridge',
onMessageReceived: (JavaScriptMessage message) {
_handleJsMessage(message.message);
},
)
..loadFlutterAsset('assets/html/test.html');
}
// 处理JS发来的消息
void _handleJsMessage(String message) {
debugPrint('JS发来消息:$message');
// 在这里根据消息内容做业务处理,比如调用原生能力、跳转页面等
}
② Dart 调用 JS 方法
Dart 可以通过 runJavaScript 直接调用 JS 中挂载在 window 上的全局方法:
dart
// 发送消息给JS
Future<void> sendMessageToJs(String method, Map<String, dynamic> params) async {
// 序列化参数为JSON,调用JS约定好的全局方法
final paramStr = jsonEncode(params);
await _controller.runJavaScript('''
window.onFlutterMessage({
method: '$method',
params: $paramStr
});
''');
}
}
③ HTML 侧配合逻辑
在你加载的 HTML 中,只需要约定好双向调用的格式,就可以和 Dart 正常通信:
html
<script>
// 注册供Flutter调用的接收方法
window.onFlutterMessage = function(data) {
console.log('Flutter发来消息:', data);
// 处理业务后返回结果
}
// 给Flutter发送消息
function sendMessageToFlutter() {
window.FlutterBridge.postMessage(JSON.stringify({
method: 'getDeviceInfo',
params: { appVersion: '1.0.0' }
}));
}
</script>
🌐 Flutter Web 场景
如果你是开发 Flutter Web 应用,需要嵌入 HTML 元素或者和页面 JS 交互,不需要 WebView,直接用 Dart 官方的 dart:js 就能实现桥接。
1. 配置依赖
在 pubspec.yaml 引入 js 包:
yaml
dependencies:
js:^0.6.7
2. 双向通信实现
① Dart 调用 JS 全局方法
dart
import 'dart:js' as js;
// 直接调用JS全局函数,传参自动适配
void callJsAlert() {
js.context.callMethod('alert', ['Hello from Dart!']);
}
// 调用自定义方法拿返回值
String getJsVersion() {
return js.context.callMethod('getAppVersion', []);
}
② 把 Dart 方法暴露给 JS 调用
用 allowInterop 把 Dart 方法包装后暴露给 window:
dart
import 'package:js/js.dart';
// 声明要挂载到window的方法
@JS('window.dartBridge')
external void setDartBridge(Function handler);
// 实际要暴露的Dart方法
void handleJsCall(String message) {
debugPrint('JS调用了Dart方法:$message');
return 'Dart处理完成';
}
void main() {
// 把方法暴露给window
setDartBridge(allowInterop(handleJsCall));
runApp(const MyApp());
}
③ JS 侧调用 Dart 方法:
javascript
// 直接调用挂载在window上的Dart方法
const result = window.dartBridge('我是JS发来的请求');
console.log('Dart返回结果:', result);
🔒 注意事项
安全校验:如果加载的是远程 HTML,一定要对消息做格式校验和来源限制,避免执行非法代码。
序列化处理:复杂对象要统一转成 JSON 字符串传递,避免类型不匹配导致的解析错误。
生命周期管理:页面销毁时要移除事件监听,避免内存泄漏。
异步处理:如果调用方法需要等待结果,可以约定回调ID,在结果返回后触发对应回调,不要阻塞主线程。
两种方案分别适配了不同场景,实际开发中根据你的运行环境选择对应方案即可,都能稳定实现 HTML 和 Dart 的双向通信。