本文采用Flutter官方WebView外掛:pub.dartlang.org/packages/we…
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(
javascriptChannels: <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://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建立完成之後,我們可以拿到一個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註釋,要注意Android端和iOS端的返回值格式是不一樣的,Android端返回的是json字串,iOS暫時只支援string和string格式的NSArray,其他型別資料還不支援。
這裡要注意的是,evaluateJavascript()
方法,Flutter建議我們在onPageFinished
回撥之後去執行,以保證所有的HTML都已經載入完畢了。因此在實際開發中,我這裡展示的這種直接將onWebViewCreated
中的controller賦值的方法是不可取的,應該是使用FutureBuilder
之類的方式去實現比較優雅(我在Gist上有完整的例子,大家可以看下)。
原始碼
- Flutter部分:gist.github.com/yumi0629/1e…
- 網頁部分:gist.github.com/yumi0629/ac…
- 除錯工具推薦使用 Amaze UI ,一個神奇的網站,一鍵生成除錯網頁,你值得擁有~~
注意:原始碼中的initialUrl
測試地址請自己生成!