欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Flutter WebView与JS互相调用简易指南

程序员文章站 2022-06-03 19:16:29
本文采用flutter官方webview插件: webview与js互相调用是一个刚需,但是貌似现在大家写的文章讲的都不是很清楚,我这个简易指南简单粗暴地分为两部分:...

本文采用flutter官方webview插件:

webview与js互相调用是一个刚需,但是貌似现在大家写的文章讲的都不是很清楚,我这个简易指南简单粗暴地分为两部分:js调用flutter和flutter调用js,拒绝花里胡哨,保证一看就懂,一学就会。

开始之前先简单了解一下官方webview所包含的api:

  • onwebviewcreated:在webview创建完成后调用,只会被调用一次;
  • initialurl:初始load的url;
  • javascriptmode:js执行模式(是否允许js执行);
  • javascriptchannels:js和flutter通信的channel;
  • navigationdelegate:路由委托(可以通过在此处拦截url实现js调用flutter部分);
  • gesturerecognizers:手势监听;
  • onpagefinished:webview加载完毕时的回调。

js调用flutter

js调用flutter有两种方法:使用javascriptchannels发送消息和使用路由委托(navigationdelegate)拦截url。

方法1:使用javascriptchannels发送消息

javascriptchannels参数可以传入一组channels,我们可以定义一个_alertjavascriptchannel变量,这个channel用来控制js调用flutter的toast功能:

javascriptchannel _alertjavascriptchannel(buildcontext context) {
 return javascriptchannel(
  name: 'toast',
  onmessagereceived: (javascriptmessage message) {
   showtoast(message.message);
  });
 }

webview(
 avascriptchannels: <javascriptchannel>[
  _alertjavascriptchannel(context),
 ].toset(),
;

在上面的代码中,我们定义了一个_alertjavascriptchannel变量,并给它起了个name叫toast,这个name属性接收的是一个字符串,它代表了js调用flutter时,双方共同商定好了的一个协议,js通过这个name去post对应的信息给flutter(api为name.postmessage('xxxxxx'))。我们在网页部分写一个简单的button,点击后开始js调用flutter的逻辑:

<button onclick="callflutter()">callflutter</button>

function callflutter(){
 toast.postmessage("js调用了flutter");
}

onmessagereceived为flutter接收到了js的消息之后的回调,我们可以通过message.message来获取js发给我们的消息内容。javascriptmessage类暂时只有一个string类型的message成员变量,所以如果需要传递复杂数据,可以通过传递json字符串来解决。

代码重点:javascriptchannel中的name要与js中的name.postmessage()相对应!!

方法2:使用路由委托navigationdelegate拦截url

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

同样的,我们在网页部分写一个简单的button,点击后跳转路由"js://webview?arg1=111&args2=222"。我们可以和客户端协商好一个scheme,比如这个例子里面就是js://webview,我们可以在query string上带上我们想要传递的参数:

<button onclick="callflutter()">callflutter</button>

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

在flutter端,我们就可以在navigationdelegate回调中拦截这个符合js://webviewscheme的路由地址了:

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;
   },

我们通过return不同的值,告诉webview怎么处理这个路由:

  • navigationdecision.prevent:阻止路由替换;
  • navigationdecision.navigate:允许路由替换。

flutter调用js

在webview创建完成之后,我们可以拿到一个webviewcontroller,通过它的evaluatejavascript()方法,我们可以执行js语句:

onwebviewcreated: (webviewcontroller webviewcontroller) {
 _controller = webviewcontroller;
},
······
floatingactionbutton: floatingactionbutton(
  onpressed: () {
   _controller
    ?.evaluatejavascript('calljs("visible")')
    ?.then((result) {
     // you can handle js result here.
    });
  },
  child: text('call js'),
  ),
 <p id="p1" style="visibility:hidden;">
 flutter 调用了 js.
 flutter 调用了 js.
 flutter 调用了 js.
 </p>
 
function calljs(message){
 document.getelementbyid("p1").style.visibility = message;
}

在上面的例子中,我们点击floatingactionbutton后,就会去执行js中的calljs()方法了,具体ui体现为:将隐藏的段落重新显示。evaluatejavascript()返回值是一个future,因此我们可以接收js给我们的返回值,返回值格式请阅读官方api注释。

这里要注意的是,evaluatejavascript()方法,flutter建议我们在onpagefinished回调之后去执行,以保证所有的html都已经加载完毕了。因此在实际开发中,我这里展示的这种直接将onwebviewcreated中的controller赋值的方法是不可取的,应该是使用futurebuilder之类的方式去实现比较优雅(我在gist上有完整的例子,大家可以看下)。

源码


调试工具推荐使用 amaze ui ,一个神奇的网站,一键生成调试网页,你值得拥有

注意:源码中的initialurl测试地址请自己生成!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。