flutter中 webview在iOS中其實是用了原生的wkwebview,所以遇骑,flutter與js的交互跟iOS原生的wkwebview與js的交互特別相似,只是叫法不同落萎。
本文采用Flutter官方WebView插件:https://pub.dartlang.org/packages/webview_flutter
另外一個插件:叫做flutter_webview_plugin,需要在"pubspec.yaml"文件中添加“flutter_webview_plugin: ^0.3.5”翔脱,并點擊右上部的“packages get”媒鼓。不過這個插件中好像對這種交互不太友好届吁,Flutter調用JS可以绿鸣,但是JS怎么調用Flutter還沒找到。
開始之前先簡單了解一下官方WebView所包含的API:
-
onWebViewCreated
:在WebView創(chuàng)建完成后調用亮蛔,只會被調用一次擎厢; -
initialUrl
:初始load的url; -
javascriptMode
:JS執(zhí)行模式(是否允許JS執(zhí)行)动遭; -
javascriptChannels
:JS和Flutter通信的Channel; -
navigationDelegate
:路由委托(可以通過在此處攔截url實現JS調用Flutter部分)沽损; -
gestureRecognizers
:手勢監(jiān)聽循头; -
onPageFinished
:WebView加載完畢時的回調炎疆。
JS調用Flutter
JS調用Flutter有兩種方法:使用javascriptChannels發(fā)送消息
和使用路由委托(navigationDelegate)攔截url
国裳。
方法1:使用javascriptChannels發(fā)送消息
javascriptChannels
參數可以傳入一組Channels,我們可以定義一個_alertJavascriptChannel變量
缝左,這個channel用來控制JS調用Flutter的toast功能:
JavascriptChannel _alertJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toast',
onMessageReceived: (JavascriptMessage message) {
showToast(message.message);
});
}
WebView(
javascriptChannels: <JavascriptChannel>[
_alertJavascriptChannel(context),
].toSet(),
在上面的代碼中,我們定義了一個_alertJavascriptChannel變量
蛇数,并給它起了個name叫Toast
是越,這個name屬性接收的是一個字符串耳舅,它代表了JS調用Flutter時倚评,雙方共同商定好了的一個協(xié)議,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發(fā)給我們的消息內容敷燎。JavascriptMessage
類暫時只有一個String類型的message成員變量,所以如果需要傳遞復雜數據焕襟,可以通過傳遞json字符串來解決。
代碼重點:JavascriptChannel中的name要與JS中的name.postMessage()相對應M依怠拄衰!
方法2:使用路由委托navigationDelegate攔截url
navigationDelegate
回調在每次網頁路由地址發(fā)生變化的時候都會觸發(fā)它褪,因此我們可以攔截特定的url來實現JS調用Flutter。
同樣的翘悉,我們在網頁部分寫一個簡單的button茫打,點擊后跳轉路由"js://webview?arg1=111&args2=222"
。我們可以和客戶端協(xié)商好一個scheme,比如這個例子里面就是js://webview
老赤,我們可以在query string上帶上我們想要傳遞的參數:
<button onclick="callFlutter()">callFlutter</button>
function callFlutter(){
/*約定的url協(xié)議為:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&args2=222";
}
在Flutter端轮洋,我們就可以在navigationDelegate
回調中攔截這個符合js://webview
scheme的路由地址了:
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創(chuàng)建完成之后,我們可以拿到一個WebViewController开财,通過它的evaluateJavascript()
方法,我們可以執(zhí)行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后碾褂,就會去執(zhí)行JS中的callJS()
方法了,具體UI體現為:將隱藏的段落重新顯示薇搁。evaluateJavascript()
返回值是一個Future斋扰,因此我們可以接收JS給我們的返回值,返回值格式請閱讀官方API注釋啃洋。
這里要注意的是传货,evaluateJavascript()
方法,Flutter建議我們在onPageFinished
回調之后去執(zhí)行宏娄,以保證所有的HTML都已經加載完畢了问裕。因此在實際開發(fā)中,我這里展示的這種直接將onWebViewCreated
中的controller賦值的方法是不可取的孵坚,應該是使用FutureBuilder
之類的方式去實現比較優(yōu)雅粮宛。