天天看點

Flutter WebView與JS互動簡易指南

本文采用Flutter官方WebView插件: https://pub.dartlang.org/packages/webview_flutter   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://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上有完整的例子,大家可以看下)。

源碼

  注意:源碼中的

initialUrl

測試位址請自己生成!

繼續閱讀