精选Flutter 中 WebView 的使用以及与 JS 交互
Posted 架构师必备
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了精选Flutter 中 WebView 的使用以及与 JS 交互相关的知识,希望对你有一定的参考价值。
WebView 的使用,算得上是比较普遍的,特别是与 JS 的交互,今天整理一下在 flutter 中使用 WebView 的一些事~
重点讲解如下两个主流插件的使用:
官方插件:webview_flutter
pub 比较好用的插件:flutter_webview_plugin
任何一个插件的使用,都是两步走:
1.引入依赖
2.导入使用,应用组件(widget)
但是这个插件的使用过程中,在 ios 里边需要单独设置一项,不然会报错。如下,在ios/Runner/Info.plist中添加
1<key>io.flutter.embedded_views_preview</key>
2<string>YES</string>*
本篇重点先练习官方插件的使用。
为了便于区分,将两个插件的使用放到了一个 dart 文件里边,如下代码:
1class WidgetWebview extends StatefulWidget {
2 String remoteUrl = "https://www.baidu.com";
3 String localUrl = "assets/html/login.html";
4 bool useWebviewFlutter = true; // 是否使用 flutter 提供的插件
5
6 @override
7 _WidgetWebviewState createState() =>
8 useWebviewFlutter ? _WebviewFlutterState() : _FlutterWebViewPluginState();
9}
10/// 方便将两个插件放在一起对比~
11abstract class _WidgetWebviewState extends State<WidgetWebview> {}
12
13class _WebviewFlutterState extends _WidgetWebviewState {
14 @override
15 Widget build(BuildContext context) {
16 return Scaffold(
17 appBar: AppBar(
18 title: Text("WebViewFlutter 与 JS 交互"),
19 ),
20 );
21 }
22}
23
24class _FlutterWebViewPluginState extends _WidgetWebviewState {
25 @override
26 Widget build(BuildContext context) {
27 return Scaffold(
28 appBar: AppBar(
29 title: Text("FlutterWebViewPlugin 与 JS 交互"),
30 ),
31 );
32 }
33}
然后本文会对 _WebviewFlutterState 进行处理
基础使用
ios中在ios/Runner/Info.plist中添加 不然页面加载不出来滴…
1<key>io.flutter.embedded_views_preview</key>
2<string>YES</string>*
首先用 WebView 来加载一个页面,很简单,只需要在 build() 方法中加入 WebView 组件即可,
如下是他的构造函数
1 const WebView({
2 Key key,
3 this.onWebViewCreated, //WebView 创建完成之后的回调
4 this.initialUrl, // 初始化 URL
5 this.javascriptMode = JavascriptMode.disabled, // JS执行模式 默认是不调用
6 this.javascriptChannels, // JS可以调用Flutter 的通道
7 this.navigationDelegate, // 路由委托(可以通过在此处拦截url实现JS调用Flutter部分) 拦截 URL
8 this.gestureRecognizers, // 手势相关
9 this.onPageFinished, // 页面加载完成的回调
10 this.debuggingEnabled = false,
11 this.userAgent,
12 this.initialMediaPlaybackPolicy =
13 AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
14 })
其实通过构造函数的简单了解,基本上学会了他的使用了,那么如何最简单的加载一个 URL 呢?
1 body: WebView(
2 initialUrl: widget.remoteUrl,
3 ),
是不是很简单呢这就是最简单的使用,用来加载一个页面啦
进阶使用
上边说了怎么简单的加载一个页面,那么继续会有一些其他的用法可能会用到呢~
WebView 有一个控制器,叫做 WebViewController,用来处理一些事情,可以在 WebView.onWebViewCreated 中获取到他。
获取页面 title
很多时候,需要将当前 flutter 页面的appbar 标题,显示为网页里边的 title,那么该怎么获取这个 title 呢?前边说的 WebViewController 内有一个 getTitle() 方法,可以用来获取网页的 title,如下:
1 /// 获取当前加载页面的 title
2 _getTitle() async {
3 String title = await _webViewController.getTitle();
4 print("title---$title");
5 }
从这获取到了 title 字符串,然后你就可以用来做处理了。
加载本地 HTML 文件
直接上代码,需要注意的地方是:
1.Bundle.loadString() 是异步执行,所以说需要用 FutureBuilder 对组件进行处理一下子。
2.要用 Uri 对文件进行转码,如代码所示
3.本地文件加入依赖时,要注意路径的准确性!此处我是在我的 assets 目录里边,创建了一个 html 的目录,将 HTML 文件放到这里边了,所以 依赖里边就得用具体到 html 路径。
1 assets:
2 - assets/
3 - assets/html/
1 @override
2 Widget build(BuildContext context) {
3 return Scaffold(
4 appBar: AppBar(
5 title: Text(widget.title),
6 ),
7 body: FutureBuilder(
8 future: _loadHtmlFile(),
9 builder: (context, snapshot) {
10 return WebView(
11 initialUrl: Uri.dataFromString(snapshot.data, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
12 .toString(),
13 onWebViewCreated: (WebViewController controller) {
14 print("webview page: webview created...");
15 _webViewController = controller;
16 _webViewController.loadUrl(
17 new Uri.dataFromString(snapshot.data, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
18 .toString());
19 },
20 onPageFinished: (url) {
21 print("webview page: load finished...url=$url");
22 _getTitle();
23 },
24 );
25 },
26 ),
27 );
28 }
JS 调用 flutter 方法
重头戏来了,在 HTML 里边来调用 flutter 中的方法,前边讲过,JS调用Flutter有两种方法:使用javascriptChannels 发送消息和使用路由委托navigationDelegate 拦截url 实现交互。
拦截的实现暂时不讲,个人觉得比较鸡肋,感兴趣的小伙伴可以自己去研究研究哈(偷笑…)
使用 javascriptChannels 发送消息
javascriptChannels 参数是一个数组形式的,意味着可以给每一个要交互的方法创建一个对应的对象(JavascriptChannel),扔到这数组里边。但是我觉得也可以考虑就创建一个对象,后续通过 JavascriptChannel 里边的键来区分处理,但是暂时不行,因为 JavascriptMessage 里边只有一个参数,还是个 string 类型,所以不太好容易处理。。。本文使用第一种方法
比如现在我要在 js 里边调用一个 flutter 的 toast 方法来显示一个 toast,那么第一步就是创建一个 JavascriptChannel如下:
1 // 创建 JavascriptChannel
2 JavascriptChannel _toastJsChannel(BuildContext context) => JavascriptChannel(
3 name: 'show_flutter_toast',
4 onMessageReceived: (JavascriptMessage message) {
5 print("get message from JS, message is: ${message.message}");
6 T.show(message.message);
7 });
1javascriptChannels: [_toastJsChannel(context)].toSet(),
要说明的地方是:
1.name 参数,对应的是一个 String 类型的键,这个就是在 JS 里边要调用的(postMessage("xxx")),必须一致。
2.onMessageReceived 回调了一个 JavascriptMessage 对象,接收来自 JS 的回调信息。目前这里边只有一个 message(String) 属性。只支持 String 类型的参数,数据过多的话可以考虑 JSON 的 String 类型参数~
接下来就是去书写简单的 JS 代码啦~~
1<div class = "rect" style="margin-top: 50px;" onclick="flutterShowToast()">JS调用Flutter</div>
2
3 /// 调用 flutter 的方法
4 /// -name:show_flutter_toast
5 function flutterShowToast() {
6 show_flutter_toast.postMessage("message from JS...");
7 }
HTML 里边的代码很简单,就是给一个 div 设置了点击事件,调了 flutterShowToast 这个方法,特别需要注意的一点是show_flutter_toast 一定不要写错了!(此处应该知道为什么必须用这个字符串哈~)
flutter 调用 JS
在WebView创建完成之后,我们可以拿到一个WebViewController,通过它的evaluateJavascript()方法,我们可以执行JS语句
1WebViewController _webViewController;
2...
3onWebViewCreated: (WebViewController controller) {
4 print("webview page: webview created...");
5 _webViewController = controller;
6 _webViewController.loadUrl(new Uri.dataFromString(snapshot.data,
7 mimeType: 'text/html',
8 encoding: Encoding.getByName('utf-8'))
9 .toString());
10 }
11...
12 floatingActionButton: FloatingActionButton(
13 child: Icon(Icons.add),
14 onPressed: (){
15 _webViewController.evaluateJavascript("flutterCallJsMethod('message from Flutter!')");
16 },
17 ),
通过在页面里边添加了一个 FloatingActionButton 来触发传递事件,如上所说,调用了evaluateJavascript 方法,内容就是 JS 里边的方法以及参数,然后在 HTML 里边对应的写一个同名的 function,就可以实现 从 flutter 到 jS 的传递了,HTML 代码如下:
1 /// Flutter 调用 JS 的此方法
2 /// 在 Flutter 中通过 _webViewController.evaluateJavascript("flutterCallJsMethod('message from Flutter!')") 调用
3 function flutterCallJsMethod(message) {
4 document.getElementById("show_info").innerText = message;
5 }
同时 evaluateJavascript 返回了一个 Future 对象,可以用来接手 JS 回传的内容。
注意一点:evaluateJavascript()方法,Flutter建议我们在onPageFinished回调之后去执行,以保证所有的HTML都已经加载完毕了。因此在实际开发中,建议参考我的写法,将 WebView 放到 FutureBuilder 里边~
以上是关于精选Flutter 中 WebView 的使用以及与 JS 交互的主要内容,如果未能解决你的问题,请参考以下文章
Flutter,WebView - 使用自定义 HTML 重建
如何在flutter中使用flutter_webview_plugin和AndroidX