本文示例程式碼可在微信公眾號「01二進位制」後臺回覆「WebView」檢視下載
前言
我們知道在開發 Native App 時經常會有開啟網頁的需求,可供的選擇通常只有兩種:
- 在 App 內部開啟網頁
- 通過呼叫系統自帶瀏覽器開啟網頁
以「微信」舉例,我們在微信內閱讀公眾號的時候就是第一種情況,但是微信同時也提供了 Open with Browser 這一選項,這就是第二種情況了。
簡單的介紹下 Android 中的 WebView
想實現第一種效果,我們需要使用一個名為 WebView 的東西,先來看看在 Android 中如何實現一個 WebView 吧。
在 Android 中我們需要先在一個 Layout 中放入 WebView 這個控制元件,然後在對應的 Activity 或者 Fragment 或者各種 Custom View 中執行一個個的 findViewById……
額,Android 開發者一定知道我在說什麼(真的很麻煩)
WebView in Flutter
Flutter 的 WebView 出現已經有一段時間了,在 Flutter 外掛社群官網搜尋 WebView 即可搜尋到比較流行的外掛,如下圖所示:
其中 webview_flutter 是官方維護的 WebView 外掛,特性是基於原生和 Flutter SDK 封裝,繼承 StatefulWidget,因此支援內嵌於 flutter Widget 樹中,這是比較靈活的;
flutter_webview_plugin 則是基於原生 WebView 封裝的 Flutter 外掛,將原生的一些基本使用 API 封裝好提供給 Flutter 呼叫,因此並不能內嵌於 Flutter Widget 樹中,因此在介面的跳轉必須得先釋放掉,返回後又要重新初始化,所以顯示會有很多限制性;
interactive_webview 則是基於 webview_flutter 封裝的 Flutter 外掛,因此原理特性上基本與官方 WebView 一致的;
在2018年 Flutter 發展初期,官方的 webview_flutter 外掛有很多問題,不過好在官方一直沒有放棄,現在的外掛已經修復了很多 bug 了,基本功能也在不斷完善中?。
flutter_webview_plugin 外掛由於其特性原因使用不靈活,因此本文我將會選擇官方提供的 webview_flutter 作為載入網頁的 WebView 外掛。
使用
webview_flutter 外掛的地址為?https://pub.flutter-io.cn/packages/webview_flutter
導包
和任何一個 Flutter package 一樣,我們需要在 pubspec.yml
中的 dependencies
下加入 webview_flutter 的 package
dependencies:
webview_flutter: ^0.3.10+4
複製程式碼
然後點選標籤欄出現的 Packages get,或者在終端輸入 Flutter package get
,順序如下圖所示:
新建一個 Widget
接下來我們新建一個 WebViewWidget,這個 Widget 接收兩個引數,分別是瀏覽器頁面標題和瀏覽頁面的 Url,我將其命名為 Browser
,並存放在 browser.dart
檔案中。
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class Browser extends StatelessWidget {
const Browser({Key key, this.url, this.title}) : super(key: key);
final String url;
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: WebView(
initialUrl: url,
javascriptMode: JavascriptMode.unrestricted,
),
);
}
}
複製程式碼
使用該頁面
在這裡我們用一個新的頁面來盛放 WebView,因此我們想使用他的時候只需要跳轉到該頁面,並傳入標題和網址即可。這裡以某個 RaisedButton 的 onPressed()
舉例
onPressed: () {
Navigator.of(context)
.push(new MaterialPageRoute(builder: (_) {
return new Browser(
url: "https://flutter-io.cn/",
title: "Flutter 中文社群",
);
}));
}
複製程式碼
對了別忘了要在 IOS 模組的 Runner 中的 info.plist 檔案中加入:
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
複製程式碼
不然這個 package 可沒辦法在 iOS 裝置上執行!
執行效果如下圖所示:
這裡只是簡單介紹 webview 在 Flutter 中的使用,其中的高階特性比如與 JavaScript 互動並沒有介紹到,有興趣的讀者可以自行查詢資料閱讀。
這就結束了嗎?
其實到這裡的時候應該是就已經結束了,但是我在使用過程中發現了一個很嚴重的問題,如果我們的 URL 是 HTTP 而不是 HTTPS 的話,那麼就只可以在 Android 9.0 以下的裝置執行(iOS同樣不可以)。
如果執行在 iOS 上會出現白屏,如果執行在 Android 9.0+ 的裝置上就會出現 net::ERR_CLEARTEXT_NOT_PERMITTED 的錯誤。
其實原因很簡單,因為無論是 iOS 還是 Android 9.0+ 都對非 HTTPS 的請求做了一些限制,下面給出我的解決方案。
iOS
我們需要在 IOS 模組的 Runner 中的 info.plist 檔案中新增如下欄位:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
複製程式碼
然後執行 flutter clean
後重新執行即可訪問 HTTP 網頁了。
Android
很抱歉,其實到現在我也沒找到在 Android 9.0+ 上通過 flutter 的 webview 訪問 HTTP 網站的辦法,我寫在這裡也是希望如果我的讀者找到了解決方案的話歡迎在評論區留言。這裡就說一下我嘗試的一些解決辦法。
其實如果是 Android 原生想解決 HTTP 限制問題有以下幾種方案:
- 切換到 HTTPS
- 將 targetSdkVersion 的版本號改到 28 以下
- 在
AndroidManifest.xml
檔案中增加android:usesCleartextTraffic="true"
配置項
第一個解決方法通常是針對自己的網站的,畢竟你總不能讓第三方網站申請 HTTPS 證照吧。
第二個解決方案在 Flutter 中是無法實現的,因為 Flutter 的執行是需要 Android SDK 28 以上的。
第三種方法我也試了,但是並沒有效果。
我查閱了很多資料,也發現了一個曲線救國的做法,就是檢測要訪問的網頁,如果是 HTTPS 的就利用 WebView 訪問,如果是 HTTP 的就呼叫第三方瀏覽器訪問。
額,這個做法吧,不好評價。
我已經在 StackOverflow 和 Flutter 的 issue 提交了問題,如果後續有解決方案,我會持續更新的。
總結
總的來說,隨著 Google 對 WebView 控制元件的不斷更新,其體驗越來越好了,使用起來相對於原生的 WebView 也更加簡便,如果你有在你的 App 內使用 WebView 的想法不妨嘗試一下?
本文示例程式碼可在微信公眾號「01二進位制」後臺回覆「WebView」檢視下載
參考
- 如何在 Flutter 中使用 WebView?- 小女 Android 工程師實驗筆記
- WebViews in Flutter – What an Amazing Breakthrough!
- Android 9: Cleartext HTTP traffic not permitted in webview