flutter实现页面多个webview的方案详解
作者:booboom
这篇文章主要为大家详细介绍了flutter如何实现页面多个webview的效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解下
场景
当我们计划使用flutter开发app时,遇到一个页面有多个由富文本编辑器编辑的html内容场景,而且内容很长很大,但是都是一些图文展示的内容。
- 使用和小程序中一样的方案对html字符串进行转换,所以使用了
flutter_html
这个库,但是结果是卡异常的卡顿,而且样式无法统一。 - 结合服务端将html字符串生成一个个单独的静态网页,如
https://www.testhtml.com/qweasdgtrtyytu.html
,文件名随机。利用官方库webview_flutter
进行渲染,因为是需要完整展示页面内容所以需要动态获取网页高度并设置flutter组件的高度(下面具体讲解),实现之后当页面webview太长时安卓闪退,由于不太懂安卓webview的机制,所以无法解决。 - 使用三方webview库
flutter_inappwebview
完美解决了以上2个方法无法解决的问题,样式统一,可设置高度,以下我们具体聊聊如何实现。
引入库,版本6.0
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
定义一个Model用来存放webview的相关信息
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, }); }
初始化相关属性
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实现
在实现之前我们也使用常规方案进行加载webview,但是同时加载多个时还是卡顿,无法达到原生的流畅效果,仔细阅读文档后发现inappwebview6.0有一个将无头浏览器模式转换为flutter widget的功能,所以使用无头模式优先加载之后在渲染到页面中,此时发现流畅了很多。
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 { // 当加载结束后进行页面样式的调整 // 执行一段js,也可以使用字符串的方式,但是作为前端还是js文件更加亲切 = =!! // detail_html.js中的内容其实就是控制页面缩放比例之类,也可以直接在生成html时带上,这里只做参考 await controller.injectJavascriptFileFromAsset(assetFilePath: "assets/js/detail_html.js"); // 获取网页的宽高并通知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内容如下:
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转化为widget 此时准备工作已经完成:
- 组件的宽高
- webview加载完成
convertFlag: 用来控制webview加载状态,加载时显示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, // 关键 onWebViewCreated: (controller) async { item.headlessWebView = null; }, ), ) : SizedBox( width: 40, height: 40, child: Center( child: CircularProgressIndicator( value: (item.progress / 100).toDouble(), ), ), ); }).toList(), ), ), );
还可以进一步优化,判断路由动画结束后再加载,这里就不详细描述了。
由于是测试代码,还有很多优化的地方,这里主要提供了一种多webview加载的优化方案。
到此这篇关于flutter实现页面多个webview的方案详解的文章就介绍到这了,更多相关flutter webview内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!