网站首页 > 知识剖析 正文
我在之前的一篇文章中解释了如何在 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 个方法:
- fromFlutter - 我们从flutter调用这个方法,用一个字符串参数表示页面新标题
- 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 部件的不同字段:
你还会注意到,我们创建了一个名为_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。
你可以从这里链接获取到本文描述的应用程序的源代码。
最后要注意的两点:
- webview_flutter包中的alert方法已经损坏
- 为了在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
延伸阅读:
Vue.js最佳静态站点生成器对比-InfoQ
关注我并转发此篇文章,即可获得学习资料~若想了解更多,也可移步InfoQ官网,获取InfoQ最新资讯~
- 上一篇: 带你重新认识身边的前端工程师
- 下一篇: 前端项目工程化之规范化代码风格
猜你喜欢
- 2024-11-18 WPS办公软件使用指南:从基础到高级功能全解析
- 2024-11-18 CSS解析器——css模块使用详解,用更优雅的方式转换css内容
- 2024-11-18 如何在Vue中使用Ueditor富文本编辑器,详情介绍如下
- 2024-11-18 增强自动插入,开源编辑器Notepad++ 6.7.0下载
- 2024-11-18 如何遍历DOM?
- 2024-11-18 解决GitHub Issue能力登顶,字节豆包MarsCode团队分享工程实践
- 2024-11-18 提高开发效率的 9 个工具
- 2024-11-18 如何在 Mac 上轻松制作好看的文本长图?
- 2024-11-18 Css字体间距的设置方法
- 2024-11-18 如何在Word文档中实现精准换行技巧
- 04-29php开发者composer使用看这一篇就够了
- 04-29引用和变量声明在不同语言中的实作
- 04-29PHP 没你想的那么差
- 04-29Ubuntu linux 上的 Nginx 和 Php 安装
- 04-29CentOS下通过yum搭建lnmp(单版本PHP)
- 04-29为什么 PHP8 是个高性能版本
- 04-29PHP8函数包含文件-PHP8知识详解
- 04-29使用无参数函数进行命令执行
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)