flutter實現(xiàn)頁面多個webview的方案詳解
場景
當(dāng)我們計劃使用flutter開發(fā)app時,遇到一個頁面有多個由富文本編輯器編輯的html內(nèi)容場景,而且內(nèi)容很長很大,但是都是一些圖文展示的內(nèi)容。
- 使用和小程序中一樣的方案對html字符串進行轉(zhuǎn)換,所以使用了
flutter_html
這個庫,但是結(jié)果是卡異常的卡頓,而且樣式無法統(tǒng)一。 - 結(jié)合服務(wù)端將html字符串生成一個個單獨的靜態(tài)網(wǎng)頁,如
https://www.testhtml.com/qweasdgtrtyytu.html
,文件名隨機。利用官方庫webview_flutter
進行渲染,因為是需要完整展示頁面內(nèi)容所以需要動態(tài)獲取網(wǎng)頁高度并設(shè)置flutter組件的高度(下面具體講解),實現(xiàn)之后當(dāng)頁面webview太長時安卓閃退,由于不太懂安卓webview的機制,所以無法解決。 - 使用三方webview庫
flutter_inappwebview
完美解決了以上2個方法無法解決的問題,樣式統(tǒng)一,可設(shè)置高度,以下我們具體聊聊如何實現(xiàn)。
引入庫,版本6.0
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
定義一個Model用來存放webview的相關(guān)信息
class WebViewModel { String title; double height; double width; HeadlessInAppWebView? headlessWebView; int progress; String url; bool convertFlag; WebViewModel({ required this.title, this.width = 0, this.height = 0, this.headlessWebView, this.progress = 0, this.url = '', this.convertFlag = false, }); }
初始化相關(guān)屬性
final List<WebViewModel> webviewList = [ WebViewModel(title: '頁面一', height: 0, width: 0, url: '', progress: 0, headlessWebView: null), WebViewModel(title: '頁面二', height: 0, width: 0, url: '', progress: 0, headlessWebView: null), WebViewModel(title: '頁面三', height: 0, width: 0, url: '', progress: 0, headlessWebView: null), ]; // 加載多個webview for (var item in webviewList) { HeadlessInAppWebView? headlessWebView = item.headlessWebView; if (headlessWebView != null && !headlessWebView.isRunning()) { headlessWebView.run(); } } // .... initWebview(WebUri("https://cdn.testhtml.com/test/html/20230801/4b6e0db8-0c4f-4a85-a664-997a4e3ed288.html"), 0); initWebview(WebUri("https://cdn.testhtml.com/test/html/20230802/ddbc1705-ca01-454c-97b6-b20a9c16e30d.html"), 1); initWebview(WebUri("https://cdn.testhtml.com/test/html/20230802/c60c5bf5-2e14-4a16-b572-d2bb2e7efaaf.html"), 2);
initWebview實現(xiàn)
在實現(xiàn)之前我們也使用常規(guī)方案進行加載webview,但是同時加載多個時還是卡頓,無法達到原生的流暢效果,仔細閱讀文檔后發(fā)現(xiàn)inappwebview6.0有一個將無頭瀏覽器模式轉(zhuǎn)換為flutter widget的功能,所以使用無頭模式優(yōu)先加載之后在渲染到頁面中,此時發(fā)現(xiàn)流暢了很多。
initWebview(WebUri url, int index) { webviewList[index].headlessWebView = HeadlessInAppWebView( initialUrlRequest: URLRequest(url: url), initialSettings: InAppWebViewSettings( verticalScrollBarEnabled: false, ), onProgressChanged: (controller, progress) async { // 獲取加載進度 setState(() { webviewList[index].progress = progress; }); }, onLoadStop: (controller, url) async { // 當(dāng)加載結(jié)束后進行頁面樣式的調(diào)整 // 執(zhí)行一段js,也可以使用字符串的方式,但是作為前端還是js文件更加親切 = =??! // detail_html.js中的內(nèi)容其實就是控制頁面縮放比例之類,也可以直接在生成html時帶上,這里只做參考 await controller.injectJavascriptFileFromAsset(assetFilePath: "assets/js/detail_html.js"); // 獲取網(wǎng)頁的寬高并通知flutter var bodyWidth = await controller.evaluateJavascript(source: "document.body.offsetWidth"); var bodyHeight = await controller.evaluateJavascript(source: "document.body.offsetHeight"); double domWidth = bodyWidth.runtimeType == double ? bodyWidth : (bodyWidth as int).toDouble(); double domHeight = bodyHeight.runtimeType == double ? bodyHeight : (bodyHeight as int).toDouble(); webviewList[index].width = domWidth; webviewList[index].height = domHeight; webviewList[index].convertFlag = true; print('=======$bodyWidth=======$bodyHeight======'); setState(() {}); }, ); }
detail_html.js內(nèi)容如下:
const head = document.querySelector('head') const meta = document.createElement('meta') const body = document.querySelector('body') meta.setAttribute('name', 'viewport') meta.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover') head.appendChild(meta) const style = document.createElement('style') style.innerHTML = ` * {margin: 0; padding: 0;} html {width: 100%; overflow-x: hidden;} body {display: inline-block; width: 100%; padding: 5px; box-sizing: border-box; } img {max-width: 100%; height: auto;} ` head.appendChild(style)
將加載好的webview轉(zhuǎn)化為widget 此時準備工作已經(jīng)完成:
- 組件的寬高
- webview加載完成
convertFlag: 用來控制webview加載狀態(tài),加載時顯示loading
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text( "HeadlessInAppWebView to InAppWebView", textScaleFactor: .8, ), ), body: SingleChildScrollView( child: Column( children: webviewList.map((item) { return item.convertFlag ? Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.all(12), height: item.height, child: InAppWebView( headlessWebView: item.headlessWebView, // 關(guān)鍵 onWebViewCreated: (controller) async { item.headlessWebView = null; }, ), ) : SizedBox( width: 40, height: 40, child: Center( child: CircularProgressIndicator( value: (item.progress / 100).toDouble(), ), ), ); }).toList(), ), ), );
還可以進一步優(yōu)化,判斷路由動畫結(jié)束后再加載,這里就不詳細描述了。
由于是測試代碼,還有很多優(yōu)化的地方,這里主要提供了一種多webview加載的優(yōu)化方案。
到此這篇關(guān)于flutter實現(xiàn)頁面多個webview的方案詳解的文章就介紹到這了,更多相關(guān)flutter webview內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android開發(fā)中Looper.prepare()和Looper.loop()
Looper用于封裝了android線程中的消息循環(huán),默認情況下一個線程是不存在消息循環(huán)(message loop)的,具體調(diào)用方法大家可以通過本文學(xué)習(xí)2016-11-11android項目從Eclipse遷移到Android studio中常見問題解決方法
android項目從Eclipse遷移到Android studio中經(jīng)常會遇到一些問題,本文提供了Android studio使用中常見問題解決方法2018-03-03Android實現(xiàn)EditText圖文混合插入上傳功能
這篇文章主要為大家詳細介紹了Android實現(xiàn)EditText圖文混合插入上傳功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08Android OpenGL ES實現(xiàn)簡單綠幕摳圖
這篇文章主要為大家介紹了Android OpenGL ES實現(xiàn)簡單綠幕摳圖示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06