写点什么

如何在 Flutter 和 JavaScript 之间创建通信桥?

  • 2020-12-31
  • 本文字数:4172 字

    阅读完需:约 14 分钟

如何在 Flutter 和 JavaScript 之间创建通信桥?

我在之前的一篇文章中解释了如何在 Android 和 iOS 中创建通信桥,作为后续,我认为解释一下如何在 Flutter 中创建通信桥也是一个不错的想法。虽然这可能看起来是一件很简单的事情,但你很快就会意识到,要使这个功能正常工作需要一些工作。


首先,重要的是意识到(在撰写本文时)Flutter 还没有内置对嵌入式 WebView 的支持。这意味着,在 Kotlin 或 Swift 中的本地应用程序中你可以实例化一个 WebView 组件,而在 Flutter 中你不能直接将 WebView 组件添加到你的应用程序中。


在创建一个新的 Flutter 项目后,我们需要使用webview_flutter包来使得能够使用 WebView。我们会向 pubspec.yaml 文件中添加依赖:


dependencies:  flutter:    sdk: flutter  webview_flutter: ^1.0.7
复制代码


然后,我们需要运行 Pub get 或者在终端中:


flutter pub get
复制代码


然后,我们需要在 main.dart 文件中导入这个包:


import 'package:webview_flutter/webview_flutter.dart';
复制代码


如果你还没有清理初始项目的代码,现在可以着手清理了。在你删除所有的注释、浮动操作按钮以及与之相关的所有内容之后,你就会剩下以下内容(为了展示,我添加了一个文本部件):


import 'dart:convert';

import 'package:flutter/material.dart';import 'package:webview_flutter/webview_flutter.dart';

void main() { runApp(MyApp());}

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Communication Bridge', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'Native - JS Communication Bridge'), ); }}

class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override _MyHomePageState createState() => _MyHomePageState();}

class _MyHomePageState extends State<MyHomePage> {

WebViewController _controller;

@override Widget build(BuildContext context) { return Text( "Flutter JS-Native Communication Bridge"
复制代码


这段代码的显示结果如下:

添加本地文件


因为我们将使用一个嵌有 JavaScript 代码的本地 html 文件,我们需要在项目中创建它。Flutter 应用程序中的所有本地资源都需要存放在一个 assets 目录中。通过右键点击左侧面板,然后选择新建->目录,来在你的主项目层创建一个 assets 目录。这个目录需要是 android 目录的一个同级目录。



然后,继续在 assets 目录中创建 index.html 文件。


<html>

<head> <title>My Local HTML File</title> </head>

<body> <h1 id="title">Hello World!</h1> <script type="text/javascript"> function fromFlutter(newTitle) { document.getElementById("title").innerHTML = newTitle; sendBack(); }

function sendBack() { messageHandler.postMessage("Hello from JS"); } </script> </body></htm
复制代码


你会注意到,我们在 html 文件的 JavaScript 部分写了 2 个方法:


  1. fromFlutter - 我们从 flutter 调用这个方法,用一个字符串参数表示页面新标题

  2. sendBack - 我们会调用这个方法来与 Flutter 通信。在这个方法中,我们会发送一个字符串消息。


稍后,我们将看看 sendBack 的内容,在那之前,我们需要先在我们的应用程序中设置好 WebView。


别忘了在 pubspec.yaml 中的 assets 部分增加 index.html(使用正确的缩进)


dependencies:  flutter:    sdk: flutter  webview_flutter: ^1.0.7  cupertino_icons: ^1.0.0

dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true assets: - assets/index.html
复制代码


设置 WebView


由于我们已经将包导入了 main.dart 文件,因此需要将文本部件替换为一个 WebView 部件。


class _MyHomePageState extends State<MyHomePage> {

WebViewController _controller;

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Webview')), body: WebView( initialUrl: 'about:blank', onWebViewCreated: (WebViewController webviewController) { _controller = webviewController; _loadHtmlFromAssets(); }, ), ); }

_loadHtmlFromAssets() async { String file = await rootBundle.loadString('assets/index.html'); _controller.loadUrl(Uri.dataFromString( file, mimeType: 'text/html', encoding: Encoding.getByName('utf-8')).toString()); }
复制代码


我们用一个 Scaffold 部件包裹 WebView(它的用途将在本文后面介绍),但是我们可以关注上面看到的 WebView 部件的不同字段:


  • initialUrl - 用来定义 WebView 指向哪里。这里我们决定将它指向无,因为我们将加载本地 html 文件。

  • onWebViewCreated - 一旦 WebView 被创建,我们将从包得到的一个回调。因为我们要保存从这个回调中获取的控制器实例,所以我们创建了一个私有成员(_controller)来存储它。


你还会注意到,我们创建了一个名为_loadHtmlFromAssets 的方法,顾名思义,它会将我们的本地 html 文件加载到 WebView 中


在这个方法中,我们使用我们的私有 WebViewController 实例,_controller,以及它的公开方法 loadUrl 来加载我们的本地 html 文件。由于这个方法中的逻辑,它的执行是异步的。


如果我们运行我们的应用程序,显示如下:



通信(Flutter -> WebView)


现在,让我们添加一些功能来调用我们在本地 html 文件中定义的 fromFlutter 方法。为此,我们将向我们的布局中增加一个浮动操作按钮(Floating Action Button,FAB),其 onPressed 方法调用 fromFlutter 方法。这也是使用 Scaffold 部件的背后原因,这样我们可以很容易地添加一个 FAB。


@override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: Text('Webview')),      body: WebView(        initialUrl: 'about:blank',        javascriptMode: JavascriptMode.unrestricted,        onWebViewCreated: (WebViewController webviewController) {          _controller = webviewController;          _loadHtmlFromAssets();        },      ),      floatingActionButton: FloatingActionButton(        child: const Icon(Icons.arrow_upward),        onPressed: () {          _controller.evaluateJavascript('fromFlutter("From Flutter")');        },      ),    );  }
复制代码


为了从 Flutter 向我们加载的 html 发起调用,我们使用 evaluateJavascript 方法。为了能使用这个方法,我们必须向我们的 WebView 增加另外一个属性,即 javascriptMode。上面,我们将这个属性设为 unrestricted(无限制的)。如果我们不设置它,我们就不能在 Flutter 和 WebView 之间通信。


反向通信(WebView -> Flutter)


还记得前面说过要讨论 senBack 方法的内容吗?现在是时候来看看了。


function sendBack() {  messageHandler.postMessage("Hello from JS");}
复制代码


在 sendBack 方法中,我们使用了一个称为 messageHandler 的对象和一个名为 postMessage 的附加方法。如果你在原生应用程序中创建过通信桥,你就会意识到,一旦你设置了一个通信桥,你就会向 Javascript 层的全局 window 对象添加一个对象用于通信。你可以随意给这个对象命名,只要你从 Javascript 向你的原生应用程序发起调用时使用那个名字就可以。


如何将这个对象添加到我们应用程序中的 Javascript 层呢?通过向我们的 WebView 部件添加一个 JavascriptChannels 属性:


class _MyHomePageState extends State<MyHomePage> {

WebViewController _controller; final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

@override Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, appBar: AppBar(title: Text('Webview')), body: WebView( initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, javascriptChannels: Set.from([ JavascriptChannel( name: 'messageHandler', onMessageReceived: (JavascriptMessage message) { _scaffoldKey.currentState.showSnackBar( SnackBar( content: Text(message) ) ); }) ]), onWebViewCreated: (WebViewController webviewController) { _controller = webviewController; _loadHtmlFromAssets(); }, ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.arrow_upward), onPressed: () { _controller.evaluateJavascript('fromFlutter("From Flutter")'); }, ), );

复制代码


我们已经定义了一个 JavascriptChannel 和一个 onMessageReceived 处理器。我们给这个通道的命名是,messageHandler,我们用它来从我们加载的本地 html 文件向我们的原生层通信。



对于目光敏锐的人,你可以已经注意到新增了一个私有变量,_scaffoldKey。这是因为我们需要给我们的 Scaffold 部件增加一个主键,以便可以显示 Snackbar。


你可以从这里链接获取到本文描述的应用程序的源代码。


最后要注意的两点:


  1. webview_flutter包中的alert方法已经损坏

  2. 为了在 iOS 中使用这个包,你必须将如下主键添加到你的 info.plist 文件中:


<key>io.flutter.embedded_views_preview</key><string>yes</string>
复制代码


如果你想要了解更多关于 Flutter 和 WebViews 的信息,你可以看看这两个有用的资源:



原文链接


How To Create a Communication Bridge Between Flutter And JavaScript


2020-12-31 15:414218

评论

发布
暂无评论
发现更多内容

工厂管理中的5个常见误区,90%的企业都踩过!

积木链小链

数字化转型 智能制造 工厂管理

YashanDB|复合索引用了也慢?IN 参数太多时建议这样调

数据库砖家

数据库

CAD三维图纸如何测量?详细教程来了

在路上

cad cad看图 CAD看图王

YashanDB|IN 参数超 300 个,查询突然变慢?问题可能出在执行计划上

数据库砖家

数据库·

CST软件如何将单端转换为差模共模

思茂信息

cst CST软件 CST Studio Suite

非计算机专业 0 门槛上手 Trae|知识库精选

火山引擎开发者社区

火山引擎

macOS Sequoia 15.5 (24F74) Boot ISO 原版可引导镜像下载

sysin

macos

YashanDB|服务器重启后数据库无法启动?别忘了这个关键服务

数据库砖家

数据库

YashanDB |YMP 迁移时报错 account lock?别让低版本 OpenSSL 坑了你

数据库砖家

智源联合南开大学开源Chinese-LiPS中文多模态语音识别数据集

智源研究院

鸿蒙HarmonyOS开发:零基础也能成为开发者

Lethehong

鸿蒙 HarmonyOS HarmonyOS NEXT DeepSeek v3 实践分享

玩转HarmonyOS NEXT网络请求:从新手到高手的实战秘籍

程序员Feri

HTTP HarmonyOS NEXT

gozero限流、熔断、降级如何实现?面试的时候怎么回答?

王中阳Go

Go gozero

YashanDB|Oracle 里的 XMLAGG 迁不过来?换成 WM_CONCAT 就对了

数据库砖家

数据库

推荐几个常用免费的文本转语音工具

石臻臻的杂货铺

CAD图纸如何进行坐标标注?

在路上

cad cad看图

理解 C# 中的各类指针

电子尖叫食人鱼

Java C#

2025“一录同行”广州首站落幕!金贝灯光赋能影视创作新浪潮

科技热闻

如何让财务规划引领企业变革,并实现持续的成本优化?

智达方通

企业管理 财务管理 财务规划

YashanDB |报错 no free block in dictionary cache?共享集群中的卡顿真凶找到了

数据库砖家

数据库

YashanDB|Oracle 中的 pipelined 表函数迁移不过?这样改写就对了

数据库砖家

数据库

京东商品详情API接口攻略

tbapi

京东API 京东商品详情API

YashanDB|UPDATE 中用子查询赋值报错?换个写法就能兼容

数据库砖家

数据库

合合信息上线智能文档处理领域首批MCP服务,助力企业快速搭建Agent

合合技术团队

人工智能 算法 #大数据

YashanDB|JDBC getString 取 DATE 字段不带时分秒?两个方法解决!

数据库砖家

数据库

会议纪要自动生成软件!AI一键搞定会议纪要,办公必备!

职场工具箱

人工智能 AI 办公软件 AIGC 会议纪要

国家信息安全等级保护三级认证定义以及流程简单讲解

行云管家

网络安全 数据安全 等保 等保测评

新疆五家等保测评机构名称以及地址汇总

行云管家

网络安全 等保 等保测评 等保测评机构

macOS Sonoma 14.7.6 (23H626) Boot ISO 原版可引导镜像下载

sysin

macos

Mint Blockchain 主网上线一周年回顾:增长之年与未来展望

NFT Research

NFT web3、

Qwen3 系列全家桶,百度百舸一键部署

百度Geek说

如何在 Flutter 和 JavaScript 之间创建通信桥?_大前端_tomerpacific_InfoQ精选文章