WebView 的使用,算得上是比較普遍的,特別是與 JS 的交互,今天整理一下在 flutter 中使用 WebView 的一些事~
重點講解如下兩個主流插件的使用:
官方插件:webview_flutter
pub 比較好用的插件:flutter_webview_plugin
任何一個插件的使用,都是兩步走:
1.引入依賴
2.導(dǎo)入使用,應(yīng)用組件(widget)
但是這個插件的使用過程中,在 IOS 里邊需要單獨設(shè)置一項,不然會報錯。如下,在ios/Runner/Info.plist中添加
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>*
本篇重點先練習(xí)官方插件的使用。
為了便于區(qū)分,將兩個插件的使用放到了一個 dart 文件里邊,如下代碼:
class WidgetWebview extends StatefulWidget {
String remoteUrl = "https://www.baidu.com";
String localUrl = "assets/html/login.html";
bool useWebviewFlutter = true; // 是否使用 flutter 提供的插件
@override
_WidgetWebviewState createState() =>
useWebviewFlutter ? _WebviewFlutterState() : _FlutterWebViewPluginState();
}
/// 方便將兩個插件放在一起對比~
abstract class _WidgetWebviewState extends State<WidgetWebview> {}
class _WebviewFlutterState extends _WidgetWebviewState {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("WebViewFlutter 與 JS 交互"),
),
);
}
}
class _FlutterWebViewPluginState extends _WidgetWebviewState {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FlutterWebViewPlugin 與 JS 交互"),
),
);
}
}
然后本文會對 _WebviewFlutterState 進(jìn)行處理
基礎(chǔ)使用
ios中在ios/Runner/Info.plist中添加 不然頁面加載不出來滴...
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>*
首先用 WebView 來加載一個頁面,很簡單,只需要在 build() 方法中加入 WebView 組件即可,
如下是他的構(gòu)造函數(shù)
const WebView({
Key key,
this.onWebViewCreated, //WebView 創(chuàng)建完成之后的回調(diào)
this.initialUrl, // 初始化 URL
this.javascriptMode = JavascriptMode.disabled, // JS執(zhí)行模式 默認(rèn)是不調(diào)用
this.javascriptChannels, // JS可以調(diào)用Flutter 的通道
this.navigationDelegate, // 路由委托(可以通過在此處攔截url實現(xiàn)JS調(diào)用Flutter部分) 攔截 URL
this.gestureRecognizers, // 手勢相關(guān)
this.onPageFinished, // 頁面加載完成的回調(diào)
this.debuggingEnabled = false,
this.userAgent,
this.initialMediaPlaybackPolicy =
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
})
其實通過構(gòu)造函數(shù)的簡單了解,基本上學(xué)會了他的使用了,那么如何最簡單的加載一個 URL 呢?
body: WebView(
initialUrl: widget.remoteUrl,
),
是不是很簡單呢這就是最簡單的使用,用來加載一個頁面啦
進(jìn)階使用
上邊說了怎么簡單的加載一個頁面,那么繼續(xù)會有一些其他的用法可能會用到呢~
WebView 有一個控制器,叫做 WebViewController,用來處理一些事情,可以在 WebView.onWebViewCreated 中獲取到他。
獲取頁面 title
很多時候,需要將當(dāng)前 flutter 頁面的appbar 標(biāo)題,顯示為網(wǎng)頁里邊的 title,那么該怎么獲取這個 title 呢?前邊說的 WebViewController 內(nèi)有一個 getTitle() 方法,可以用來獲取網(wǎng)頁的 title,如下:
/// 獲取當(dāng)前加載頁面的 title
_getTitle() async {
String title = await _webViewController.getTitle();
print("title---$title");
}
從這獲取到了 title 字符串,然后你就可以用來做處理了。
加載本地 HTML 文件
直接上代碼,需要注意的地方是:
1.Bundle.loadString() 是異步執(zhí)行,所以說需要用 FutureBuilder 對組件進(jìn)行處理一下子。
2.要用 Uri 對文件進(jìn)行轉(zhuǎn)碼,如代碼所示
3.本地文件加入依賴時,要注意路徑的準(zhǔn)確性!此處我是在我的 assets 目錄里邊,創(chuàng)建了一個 html 的目錄,將 HTML 文件放到這里邊了,所以 依賴?yán)镞吘偷糜镁唧w到 html 路徑。
assets:
- assets/
- assets/html/
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: _loadHtmlFile(),
builder: (context, snapshot) {
return WebView(
initialUrl: Uri.dataFromString(snapshot.data, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
.toString(),
onWebViewCreated: (WebViewController controller) {
print("webview page: webview created...");
_webViewController = controller;
_webViewController.loadUrl(
new Uri.dataFromString(snapshot.data, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
.toString());
},
onPageFinished: (url) {
print("webview page: load finished...url=$url");
_getTitle();
},
);
},
),
);
}
JS 調(diào)用 flutter 方法
重頭戲來了,在 HTML 里邊來調(diào)用 flutter 中的方法,前邊講過,JS調(diào)用Flutter有兩種方法:使用javascriptChannels 發(fā)送消息和使用路由委托navigationDelegate 攔截url 實現(xiàn)交互。
攔截的實現(xiàn)暫時不講,個人覺得比較雞肋,感興趣的小伙伴可以自己去研究研究哈(偷笑...)
使用 javascriptChannels 發(fā)送消息
javascriptChannels 參數(shù)是一個數(shù)組形式的,意味著可以給每一個要交互的方法創(chuàng)建一個對應(yīng)的對象(JavascriptChannel),扔到這數(shù)組里邊。但是我覺得也可以考慮就創(chuàng)建一個對象,后續(xù)通過 JavascriptChannel 里邊的鍵來區(qū)分處理,但是暫時不行,因為 JavascriptMessage 里邊只有一個參數(shù),還是個 string 類型,所以不太好容易處理。。。本文使用第一種方法
比如現(xiàn)在我要在 js 里邊調(diào)用一個 flutter 的 toast 方法來顯示一個 toast,那么第一步就是創(chuàng)建一個 JavascriptChannel如下:
// 創(chuàng)建 JavascriptChannel
JavascriptChannel _toastJsChannel(BuildContext context) => JavascriptChannel(
name: 'show_flutter_toast',
onMessageReceived: (JavascriptMessage message) {
print("get message from JS, message is: ${message.message}");
T.show(message.message);
});
javascriptChannels: [_toastJsChannel(context)].toSet(),
要說明的地方是:
1.name 參數(shù),對應(yīng)的是一個 String 類型的鍵,這個就是在 JS 里邊要調(diào)用的(postMessage("xxx")),必須一致。
2.onMessageReceived 回調(diào)了一個 JavascriptMessage 對象,接收來自 JS 的回調(diào)信息。目前這里邊只有一個 message(String) 屬性。只支持 String 類型的參數(shù),數(shù)據(jù)過多的話可以考慮 JSON 的 String 類型參數(shù)~
接下來就是去書寫簡單的 JS 代碼啦~~
<div class = "rect" style="margin-top: 50px;" onclick="flutterShowToast()">JS調(diào)用Flutter</div>
/// 調(diào)用 flutter 的方法
/// -name:show_flutter_toast
function flutterShowToast() {
show_flutter_toast.postMessage("message from JS...");
}
HTML 里邊的代碼很簡單,就是給一個 div 設(shè)置了點擊事件,調(diào)了 flutterShowToast 這個方法,特別需要注意的一點是show_flutter_toast 一定不要寫錯了!(此處應(yīng)該知道為什么必須用這個字符串哈~)
flutter 調(diào)用 JS
在WebView創(chuàng)建完成之后,我們可以拿到一個WebViewController,通過它的evaluateJavascript()方法,我們可以執(zhí)行JS語句
WebViewController _webViewController;
...
onWebViewCreated: (WebViewController controller) {
print("webview page: webview created...");
_webViewController = controller;
_webViewController.loadUrl(new Uri.dataFromString(snapshot.data,
mimeType: 'text/html',
encoding: Encoding.getByName('utf-8'))
.toString());
}
...
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
_webViewController.evaluateJavascript("flutterCallJsMethod('message from Flutter!')");
},
),
通過在頁面里邊添加了一個 FloatingActionButton 來觸發(fā)傳遞事件,如上所說,調(diào)用了evaluateJavascript 方法,內(nèi)容就是 JS 里邊的方法以及參數(shù),然后在 HTML 里邊對應(yīng)的寫一個同名的 function,就可以實現(xiàn) 從 flutter 到 jS 的傳遞了,HTML 代碼如下:
/// Flutter 調(diào)用 JS 的此方法
/// 在 Flutter 中通過 _webViewController.evaluateJavascript("flutterCallJsMethod('message from Flutter!')") 調(diào)用
function flutterCallJsMethod(message) {
document.getElementById("show_info").innerText = message;
}
同時 evaluateJavascript 返回了一個 Future 對象,可以用來接手 JS 回傳的內(nèi)容。
注意一點:evaluateJavascript()方法,F(xiàn)lutter建議我們在onPageFinished回調(diào)之后去執(zhí)行,以保證所有的HTML都已經(jīng)加載完畢了。因此在實際開發(fā)中,建議參考我的寫法,將 WebView 放到 FutureBuilder 里邊~
完事暫時就先整理這么多了希望對大家有用吧~