本文采用Flutter官方WebView插件: https://pub.dartlang.org/packages/webview_flutter WebView與JS互相調用是一個剛需,但是貌似現在大家寫的文章講的都不是很清楚,我這個簡易指南簡單粗暴地分為兩部分: JS調用Flutter Flutter調用JS
和
,拒絕花裡胡哨,保證一看就懂,一學就會。
開始之前先簡單了解一下官方WebView所包含的API:
-
:在WebView建立完成後調用,隻會被調用一次;onWebViewCreated
-
:初始load的url;initialUrl
-
:JS執行模式(是否允許JS執行);javascriptMode
-
:JS和Flutter通信的Channel;javascriptChannels
-
:路由委托(可以通過在此處攔截url實作JS調用Flutter部分);navigationDelegate
-
:手勢監聽;gestureRecognizers
-
:WebView加載完畢時的回調。onPageFinished
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://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注釋。
這裡要注意的是,
evaluateJavascript()
方法,Flutter建議我們在
onPageFinished
回調之後去執行,以保證所有的HTML都已經加載完畢了。是以在實際開發中,我這裡展示的這種直接将
onWebViewCreated
中的controller指派的方法是不可取的,應該是使用
FutureBuilder
之類的方式去實作比較優雅(我在Gist上有完整的例子,大家可以看下)。
源碼
- Flutter部分: https://gist.github.com/yumi0629/1e7e1061734fcd4de07ca73a13d83846
- 網頁部分: https://gist.github.com/yumi0629/ac8505c469f6eaa8e600b85927f326fb
- 調試工具推薦使用 Amaze UI ,一個神奇的網站,一鍵生成調試網頁,你值得擁有~~
注意:源碼中的
initialUrl
測試位址請自己生成!