粮草机器人 原生 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 的双向通信。