详解Flutter 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测试地址请自己生成!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。