为了账号安全,请及时绑定邮箱和手机立即绑定

【备战春招】第18天 基于自定义WebView实现H5混合开发

标签:
Android

课程名称Flutter从入门到进阶 实战携程网App 一网打尽核心技术
课程章节:Flutter进阶实战:App首页功能开发
课程讲师CrazyCodeBoy

课程内容

在 Flutter 中使用 webview

在 Flutter 中与 H5混合开发也是常用的功能,在 Flutter 中使用的比较多的插件有webview_flutter和flutter_webview_plugin。

  • webview_flutter,是官方维护的 WebView 插件,特性是基于原生和 Flutter SDK 封装,继承 StatefulWidget,因此支持内嵌于 flutter Widget 树中,使用比较灵活。
  • flutter_webview_plugin,则是基于原生 WebView 封装的 Flutter 插件,将原生的一些基本使用 API 封装好提供给 Flutter 调用,因此并不能内嵌于 Flutter Widget 树中,因此在界面的跳转必须得先释放掉,返回后又要重新初始化。

使用时,在 Android 设备上还会遇到这样的问题,Android9开始谷歌就默认不让开发者访问不安全HTTP内容了,如果非要用HTTP,那必须在networkSecurityConfig里配置cleartextTrafficPermitted才行。具体做法是:需要在AndroidManifest.xml里配置好usesCleartextTraffic和network_security_config。

在flutter项目下创建/android/app/src/main/res/xml/network_security_config.xml文件,填上配置内容

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="system" />
            <certificates class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="user" />
        </trust-anchors>
    </base-config>
</network-security-config>

然后修改/android/app/src/main/AndroidManifest.xml文件,在application节点加入以下两个属性:

android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"

之后在控制台执行flutter clean,重新run,就可以看到App里的webview能正常打开HTTP网站了。

Flutter与 H5之间的交互

在Flutter 使用 webview 打开网页后,很多时候还会面临与 H5 进行交互的问题。系统的 webview 主要包括下面的方法:

const WebView({
    Key? key,
    this.onWebViewCreated,  //在WebView创建完成后调用,只会被调用一次
    this.initialUrl,    //初始load的url
    this.initialCookies = const <WebViewCookie>[],
    this.javascriptMode = JavascriptMode.disabled,  //JS执行模式(是否允许JS执行)
    this.javascriptChannels,    //JS和Flutter通信的Channel
    this.navigationDelegate,    //路由委托(可以通过在此处拦截url实现JS调用Flutter部分)
    this.gestureRecognizers,    //手势监听
    this.onPageStarted,     //WebView开始加载时的回调
    this.onPageFinished,    //WebView加载完毕时的回调
    this.onProgress,    //WebView加载进度的回调
    this.onWebResourceError,
    this.debuggingEnabled = false,
    this.gestureNavigationEnabled = false,
    this.userAgent,
    this.zoomEnabled = true,
    this.initialMediaPlaybackPolicy =
        AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
    this.allowsInlineMediaPlayback = false,
    this.backgroundColor,
  }) 

H5调用Flutter中的方法

JS调用Flutter有两种方法:使用javascriptChannels发送消息和使用路由委托(navigationDelegate)拦截url。

使用javascriptChannels发送消息

javascriptChannels参数可以传入一组Channels,可以定义一个_alertJavascriptChannel变量,这个channel用来控制JS调用Flutter中的方法。

Flutter 端实现:

JavascriptChannel _alertJavascriptChannel(BuildContext context) {
    return JavascriptChannel(
        name: 'Toast',
        onMessageReceived: (JavascriptMessage message) {
          showToast(message.message);
        });
  }

WebView(
    javascriptChannels: <JavascriptChannel>[
        _alertJavascriptChannel(context),
    ].toSet(),
;

H5 端实现:

<button onclick="callFlutter()">callFlutter</button>
function callFlutter(){
   Toast.postMessage("JS调用了Flutter");
}

在上面的代码中定义了一个_alertJavascriptChannel变量,并给它起了个name叫Toast,这个name属性接收的是一个字符串,它代表了JS调用Flutter时,双方共同商定好了的一个协议,JS通过这个name去post对应的信息给Flutter。onMessageReceived为Flutter接收到了JS的消息之后的回调,通过message.message来获取JS发给flutter的消息内容。JavascriptMessage类暂时只有一个String类型的message成员变量,所以如果需要传递复杂数据,可以通过传递json字符串来解决。

需要注意的是:JavascriptChannel中的name要与JS中的name.postMessage()相对应。

使用路由委托navigationDelegate拦截url

navigationDelegate回调在每次网页路由地址发生变化的时候都会触发,因此可以拦截特定的url来实现JS调用Flutter。

H5 端实现:

<button onclick="callFlutter()">callFlutter</button>
function callFlutter(){
  /*约定的url协议为:js://webview?arg1=111&arg2=222*/
  document.location = "js://webview?arg1=111&args2=222";
}

Flutter端实现:

navigationDelegate: (NavigationRequest request) {
            if (request.url.startsWith('js://webview')) {
              showToast('JS调用了Flutter By navigationDelegate');
              print('blocking navigation to $request}');
              return NavigationDecision.prevent;
            }
            print('allowing navigation to $request');
            return NavigationDecision.navigate;
          },

H5端与 Flutter 端确定好 scheme,在query string上带上想要传递的参数,在Flutter端,可以在navigationDelegate回调中拦截这个 scheme的路由地址。navigationDelegate()方法的不同返回值,告诉WebView怎么处理这个路由:NavigationDecision.prevent(阻止路由替换)、NavigationDecision.navigate:(允许路由替换)。

Flutter调用H5中的方法

在WebView创建完成之后,在 Flutter 端可以拿到一个WebViewController,通过它的evaluateJavascript()方法,可以执行JS语句。

evaluateJavascript()返回值是一个Future,因此可以接收JS的返回值,要注意Android端和iOS端的返回值格式是不一样的,Android端返回的是json字符串,iOS暂时只支持string和string格式的NSArray。

课程总结

这一讲主要介绍了 Flutter 中通过自定义 webview 的方式,实现 Flutter 中加载 H5 的功能。
图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
11
获赞与收藏
16

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消