0
点赞
收藏
分享

微信扫一扫

Uniapp跟原生android插件交互发信息(二)

yongxinz 2024-12-18 阅读 34

前言

欢迎来到第二篇文章,这也是第二个难题,就是原有的移动端本身一些页面H5的形式去呈现(webview),例如某些需要动态更换内容的页面,某些活动页面、支付页面,不仅仅做页面呈现,还包括一些数据交互,那么在项目初期,我们尝试使用过了官方提供的webview,但是功能比较有限,因此我们选择了flutter_inappwebview这个插件

  • 官网webview
https://pub.dev/packages/webview_flutter
  • flutter_inappwebview
https://pub.dev/packages/flutter_inappwebview

本文以flutter_inappwebview为案例,将陈述我将Flutter编译成web之后,如果去处理flutter_inappwebview的。

原理

我想在讲之前,先讲讲什么是webview?

实际上对我来说,webview应该相对于原生端而已(ios、android),是由原生提供的底层网页环境容器,去呈现网页内容,那么不同端,他们提供的环境当然是不一样的,因为网页内核不一样,那么也会造成很多的差异,所以为了抹平这些差异,才会提供第三方插件,封装公共的api供顶层调用,底层由各个平台去进行实现

例如我之前的文章提到过的数据交互,本质上就是在当前环境去注入某些方法、某些类去给网页容器提供交互的能力。

那么对于flutter_inappwebview而言,它给每一个网页容器提供了一个控制器,这个控制器有一个addJavaScriptHandler方法,可以连接到网页容器,实现监听和传输数据,同时给网页容器放了一个公共的类window.flutter_inappwebview类提供网页传输数据到客户端的能力。

那么我说webview应该相对于原生端而已,因为Flutter在编译成web之后,本质上就是web,那么之前的网页就不能叫webview,应该叫iframe

那么我们的问题,实际上就是web和iframe的数据交互问题

实现

那么,如果你明白本质是web和iframe的数据交互问题,你就明白为什么addJavaScriptHandler方法在web平台没有实现的原因,因为压根就不需要注入,他们本身就已经在一个环境下面了。

由于浏览器有同源策略,所以我们还是使用postMessgae的方法,但有一些地方需要注意。

实际上flutter_inappwebview有对web端的某些api进行封装,编译成web,那么之前的iframe的配置在flutter_inappwebview的options里面也会存在。


  InAppWebViewSettings getInAppWebViewGroupOptions() {

    return InAppWebViewSettings()
       ..useShouldInterceptAjaxRequest = false
       ..useShouldInterceptFetchRequest = false
       ..supportZoom = true
       ..clearCache = true
      ..iframeName = 'my_ifram' //设定ifame的名称,这个就是给web环境使用的
       ..mediaPlaybackRequiresUserGesture = true
      ..transparentBackground = true
      ..allowsInlineMediaPlayback = false;
   }

同样验证的方式,你可以编译成web之后,之前的webview,在浏览器标签上也可以得知上iframe。

和iframe通信的方式网上有很多方法,我这里不过多赘述。

父页面发送:

 //获取子iframe,通过name属性或者直接拿第一个iframe
      web.HTMLIFrameElement childIframe = web.document
          .getElementById("flutter_inappwebview-0") as web.HTMLIFrameElement;
      Map data = {
        'user': {'token': 1111}
      };
      JSString msg = jsonEncode(data).toJS;
      //给子ifram传递数据
      // childIframe.contentWindow?.postMessage(msg, '*'.toJS);//这种方式是不行的
      childIframe.contentWindowCrossOrigin?.postMessage(msg, '*'.toJS);

父页面接收:

//监听子组件传递过来的消息
      web.window.addEventListener('message', callback.toJS);
  void callback(web.Event e) {
    print("收到js交互=====${e}");
  }

iframe发送:

window.top.postMessage('hello', '*')

iframe接收:

window.addEventListener('message', (event) => {
				console.log("ssss", event.data)})

难点

条件编译

尽管Flutter支持web端

但当编译成web端时,此时如果使用到了io库(Platform相关)都会在运行时报错。

当编译成原生端,使用web端相关的库(web、js库)同样也会在运行时报错。

那么你就需要进行文件的替换工作,让Flutter在编译时,根据不同的端去引入不同的文件。

比如这一段代码:

     web.window.addEventListener('message', callback.toJS);
  void callback(web.Event e) {
    print("收到js交互=====${e}");
  }

这里我们需要一定要使用到web库相关的内容,才有window环境以及调用addEventListener,但是你在原生端,这段代码会让你在编译时就报错了。
因此你需要去做文件替换。

自己创建两个文件:

import './vm/ai_web_js.dart' if (kIsWeb) 'dart:js_interop';
import './vm/ai_web_window.dart' if (kIsWeb) 'package:web/web.dart' as web;

在原生端使用的是我们自己写的文件,实现的是空方法。

在web端使用对应的库文件

链接为hash链接

尽管这种方式解决了编译和运行的问题,但是却失去了相应的代码提示,如果你有更好的方法,欢迎在下方提出,下一篇会解决Dio以及请求相关的问题

举报

相关推荐

0 条评论