Flutter 中 JavaScript(JS)与 Dart 双向通信实现方案

Flutter 中 JavaScript(JS)与 Dart 双向通信实现方案

文章目录

在这里插入图片描述

基于官方 webview_flutter 插件的通信方式

基于官方 webview_flutter 插件的通信方式

在 pubspec.yaml 中添加

添加 webview 包 方便后面使用,记得 pub get 更新

dependencies: flutter: sdk: flutter # 移动端 WebView 通信核心依赖 webview_flutter: ^4.4.4 # Web 平台 JS 通信依赖 js: ^0.6.7 

配置自己使用的 js 路径地址信息, 该地址位于项目根目录中添加对应 js 文件

flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: assets: - assets/js/xxx.js 
静态 js 文件,建立通道
function test(){ if (window.FlutterJsChannel) { window.FlutterJsChannel.postMessage(realTimeData); console.log(realTimeData); // 新增:打印推送成功日志 console.log("【JS 推送成功】:数据已发送给 Flutter"); } else { // 新增:打印推送失败日志 console.log("【JS 推送失败】:FlutterJsChannel 不存在"); } } 

postMessage 通道的核心概念

  • postMessage 本质是 Flutter 为 WebView 提供的跨端通信接口,专门用于 JavaScript → Flutter 的数据传递(单向主动推送)。你可以把它理解成:
  • Flutter 在 WebView 里 “预埋” 了一个名为 FlutterJsChannel(你自定义的名称)的 “消息信箱”;
  • JS 端只要往这个 “信箱” 里丢消息(调用 postMessage),Flutter 端的 onMessageReceived 回调就会立刻收到并处理。

WebView 加载的 HTML/JS 中,通过 window.通道名称.postMessage(数据) 发送消息

dart 文件 创建 web 交互 方法定义

通过 runJavaScript(jsCode); 调用项目 内部方法

import'package:webview_flutter/webview_flutter.dart'; late WebViewController _webViewController; _webViewController =WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted)..setBackgroundColor(Colors.white)..loadHtmlString("""<html><body></body></html>""")..addJavaScriptChannel("FlutterJsChannel",// 实时接收 JS 传递的数据(持续回调,无需主动触发) onMessageReceived:(JavaScriptMessage message){print("object");// 第一步:先打印日志,这是最直接的验证,无关 UI 更新print("【通道回调触发】:收到数据 -> ${message.message}");// print("【数据类型】:${message.message.runtimeType}");_updateAnimeList(message.message);});// 步骤 1:读取并加载 Assets 中的 JS 文件(核心:注入 JS 函数)Future<void>_loadAssetsJsFile()async{try{// ① 读取 Assets 中的 JS 文件(转为纯文本字符串)// 路径必须和 pubspec.yaml 中声明的一致(大小写敏感)String assetsJsContent =await rootBundle.loadString("assets/js/md5.js");// ② 执行该 JS 字符串,将函数注入 WebView 环境// 此时只是「导入」函数,并未调用具体方法,无返回值await _webViewController.runJavaScript(assetsJsContent);setState((){ _callResult ="Assets JS 文件加载完成(函数已注入),可调用具体方法";_callAssetsJsFunction();print(_callResult);});}catch(e){setState((){ _callResult ="加载 Assets JS 文件失败:$e";});}}// 步骤 2:调用 Assets JS 文件中定义的具体方法_callAssetsJsFunction()async{try{// ① 编写 JS 代码,调用已注入的函数(来自 assets/js/demo.js)String jsCode =""" test(); """;// ③ 执行 JS 代码,触发函数调用 _webViewController.runJavaScript(jsCode);// 新增:打印日志,确认 JS 指令发送成功print("【Flutter】JS 执行指令已发送给 WebView");}catch(e){setState((){ _callResult ="调用 Assets JS 函数失败:$e";});}}
  • WebViewController()
    • 创建一个 WebView 的控制器实例,用来管理 WebView 的所有行为(加载页面、JS 交互、样式设置等)。
  • …setJavaScriptMode(JavaScriptMode.unrestricted)
    • 开启 WebView 的 JavaScript 支持:
    • JavaScriptMode.unrestricted:完全允许 JS 执行(默认是禁用的),这是 Flutter 和 JS 交互的前提。
    • 如果设为JavaScriptMode.disabled,则 WebView 中的 JS 代码无法运行,通信通道也会失效。
  • …setBackgroundColor(Colors.white)
    • 设置 WebView 的背景颜色为白色(默认可能是透明或灰色),优化视觉效果。
  • ..loadHtmlString("""<html><body></body></html>""")
    • 加载一段空的 HTML 字符串到 WebView 中(也可以用loadUrl加载远程网页)。这里加载空页面,说明核心目的是通过 JS 通道接收数据,而非展示网页内容。
  • …addJavaScriptChannel(…)
    • 这是核心逻辑:创建 Flutter 和 JS 之间的通信通道,实现JS 主动向 Flutter 发送数据:
    • “FlutterJsChannel”:通道的唯一标识,JS 端必须通过这个名称调用(比如window.FlutterJsChannel.postMessage(‘要传递的数据’))。
    • onMessageReceived:回调函数,只要 JS 调用了postMessage,这个回调就会实时触发(也就是你注释里说的 “持续回调,无需主动触发”)。
    • JavaScriptMessage message:回调的参数,message.message是 JS 传递的原始数据(字符串类型)。
  • 回调内的逻辑
    • print(“【通道回调触发】:收到数据 -> ${message.message}”):打印 JS 传递的数据,用于调试验证。
    • _updateAnimeList(message.message):调用自定义方法处理数据(比如解析数据、更新动漫列表)。
    • 注释的setState:如果需要更新 Flutter 的 UI(比如把数据展示在页面上),必须用setState触发页面刷新。
项目启动类
void main() { WidgetsFlutterBinding.ensureInitialized(); runApp(const MyApp()); } 

WidgetsFlutterBinding.ensureInitialized() 是 Flutter 应用的 “基础引擎初始化”,为所有 Widget 和异步操作提供运行环境。

Web 平台 - Dart 与 JS 通信

@JS

@JS() 是 package:js 库提供的元注解(装饰器),作用是建立 Dart 代码和 JS 代码的映射关系。你可以把它理解成:给 Dart 代码贴一个 “标签”,告诉 Dart 编译器 “这段代码要对应到 JS 中的某个东西”。

传递 Dart 函数给 JS(回调)

用 @allowInterop 注解(@JS() 配套),可以把 Dart 函数作为回调传给 JS。

  • @JS() 是 Dart 与 Web 端 JS 交互的核心注解,用于建立 Dart 代码和 JS 变量 / 函数 / 对象的映射关系。
  • 使用时必须导入 package:js,且被注解的成员需加 external 关键字。
  • 配套 @allowInterop 可实现 Dart 函数作为回调传给 JS,仅支持 Web 平台。

Read more

C++微服务实战中好友管理子服务的全面解析

C++微服务实战中好友管理子服务的全面解析

【C++ 微服务实战】IM 好友管理子服务全解析:从 Proto 定义到高可用部署 在即时通讯(IM)系统中,好友管理子服务是连接 “用户社交关系” 与 “聊天会话” 的核心枢纽 —— 它既要处理好友申请、关系维护,也要管理单聊 / 群聊会话的创建与成员维护。本文基于实际项目代码(C++/brpc/Protobuf/ODB),从 “接口设计”“数据模型”“核心逻辑”“高可用部署” 四个维度,完整拆解好友管理子服务的实现细节,带你理解如何构建一个解耦、可靠的微服务。 一、服务定位与技术栈 在 IM 微服务架构中,好友管理子服务(Friend Server)的核心职责是 **“管理用户社交关系” 与 “维护聊天会话容器”**,向上对接网关服务接收客户端请求,向下依赖 MySQL/ES 存储数据,

By Ne0inhk
Microsoft Visual C++ 运行库安装教程(2025 最新版全版本修复指南)

Microsoft Visual C++ 运行库安装教程(2025 最新版全版本修复指南)

前言 在使用大型软件、开发工程项目或玩 3A 游戏时,很多人都遇到过这样的报错: “缺少 msvcp140.dll” “无法继续执行代码,因为系统找不到 vcruntime140_1.dll” “程序无法启动,因为计算机中丢失了 MSVCR100.dll” 这些提示看似复杂,其实本质是 Microsoft Visual C++ 运行库(VC++ Redistributable)缺失或损坏 所致。 本文将带来 2025 年最新版 Microsoft Visual C++ 运行库安装教程,无论你是游戏玩家、开发者还是普通用户,都能找到最合适的解决方案。内容涵盖: * 一键修复方法(适合新手,快速解决 DLL 报错) * 手动下载安装方案(适合专业或开发用途) * 常见 DLL 报错与完整修复思路 * 系统维护与预防技巧

By Ne0inhk

【3D 打印避坑实录】如何用 Blender 彻底修复空壳 STL

关键词:3D 打印 / Cura / Blender / Shells / Infill / STL 修复 适用人群:3D 打印新手 & 被“怎么调填充都没用”折磨过的人 一、问题现象:不管怎么调 Infill,模型内部都是空的 很多人在 Cura 里会遇到这样的问题: * Infill 从 10% 调到 100% * 预览里模型依然是空的 * 有的地方“看起来有填充”,有的地方完全没有 * 甚至同一个模型,切一刀有实有空 一开始很容易误以为是 Cura 的参数问题。 但事实是: 👉 这不是切片问题,而是模型本身的问题。 二、核心原因:Cura 不知道“哪里是内部” Cura 的判断逻辑其实非常简单: 只有当 STL

By Ne0inhk
【Linux】线程池(二)C++ 手写线程池全流程:从核心设计到线程安全、死锁深度解析

【Linux】线程池(二)C++ 手写线程池全流程:从核心设计到线程安全、死锁深度解析

文章目录 * 实现线程池 * ThreadPool类设计 * 构造函数 * Start接口 * 线程池接入日志 * 初步实现源码及效果图 * 总结代码执行逻辑 * 实现回调函数Routine * enqueue接口实现 * 线程池退出stop接口优化 * 线程池源码 * 线程安全和重入问题 * 结论 * 死锁 * 死锁四个必要条件 * 避免死锁 * STL、智能指针和线程安全 实现线程池 我们之前已经接触了进程池,其实线程池和进程池核心思路差不多,对于线程池来说,会有一个任务队列和若干线程,用户往任务队列里添加任务,若干线程在任务队列里拿任务并完成。 ThreadPool类设计 构造函数 对于线程来说,启动线程池分为两步: 1.先创建线程本身(Thread类对象)2.再启动线程(调用Thread的start接口) 所以在构造函数我们要先创建线程本身(thread t(回调函数,线程名)),创建线程需要传递回调函数(假设是hello)和线程名,但这里有一个问题,一般来说传递的

By Ne0inhk