flutter 解决webview加载重定向h5页面 返回重复加载问题

long time no see. 如果觉得该方案helps,点个赞,评论打个call,这是我前进的动力~

通常写法:

项目里用的webview_flutter
正常webview处理返回事件

if (await controller.canGoBack()) {controller.goBack();
} else {Navigator.pop(context);
}

就是h5历史栈,一直退栈,如果栈内元素只有一个了,就直接关闭webview的页面了。


问题描述:

正常情况是没问题的的。
比如A-->B-->C,一直触发返回事件的话,逻辑是C-->B,B-->A, A直接关。
如果h5里有重定向的话,就有问题了。
比如A(A1重定向到A2)-->B-->C,一直触发返回事件的话,逻辑是C-->B,B-->A2, A2-->A1-->A2,A2-->A1-->A2...
导致webview界面一直退不出来。

解决方案:

参考https://github.com/flutter/flutter/issues/137737,拉到最下面
设定pageFinished后xxx毫秒内NavigationRequest触发,判定为重定向。逻辑:已知A1重定向A2,此时触发返回事件,A2返回到A1,在A1准备重定向到A2的时候,根据条件判断为重定向然后进行阻断,并再次执行一次返回逻辑。
另外该issue原始代码还是有问题,没有考虑到NavigationRequest可能跑在onPageFinished前面,故自己添加了轮询等待的代码。
注意:这只是workaround,极端情况下并不能做到100%可靠。必要情况可以考虑跟h5相关开发,约定不用重定向或改用其它方案。

自己在android设备上实测了下,还是挺稳定的。

几种可以考虑的方案:
1.修改flutter_webview源码,上传到github,然后在自己的仓库引用该库。(该方案可以自己去修改到android测和ios测的相关代码,比如flutter_webview没提供忽略ssl证书报错和ssl证书检查的问题就可以通过该方式解决,感兴趣的话可以上网查一查)
2.换webview的库,比如用flutter_inappwebview,该库提供更强大的原生api支持,围绕这个库的api来尝试解决。也是很流行的库,但不是官方flutter.dev出品。

解决代码:

如下

class WebPageContainer extends StatefulWidget {const WebPageContainer({super.key});@overrideState<WebPageContainer> createState() => _WebPageContainerState();
}class _WebPageContainerState extends State<WebPageContainer> {late WebViewController controller;String url = '';bool _backEventTriggered = false;DateTime? _lastedPageFinishedTime;bool _pageIsFinished = false;@overridevoid initState() {super.initState();}@overridevoid didChangeDependencies() {final Map<String, dynamic>? arguments = ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?;if (arguments != null) {url = arguments['url'] ?? '';debugPrint('third---url:$url');}super.didChangeDependencies();_initWebViewController();}// web端调用// <button onclick="jump()">打开一个新的webpage</button>// function jump() {//   var msg = "https://www.baidu.com"//   if (toNewWebPage) {//     toNewWebPage.postMessage(msg);//   }// }// getStatusBarHeight用法// h5页面调用getStatusBarHeight,同上// h5页面同时要定义onStatusBarHeightReceived,该方法是flutter测获取完高度后调用的// 例如:// function onStatusBarHeightReceived(height) {//   // 显示状态栏高度//   document.getElementById('statusBarHeight').innerText = 'Status Bar Height: ' + height;// }void _initWebViewController() {controller = WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted)..setBackgroundColor(const Color(0x00000000))..setNavigationDelegate(NavigationDelegate(onProgress: (int progress) {// debugPrint('WebPage onProgress $progress');},onPageStarted: (String url) {_pageIsFinished = false;debugPrint('WebPage onPageStarted $url');},onPageFinished: (String url) async {debugPrint('WebPage onPageFinished $url');_pageIsFinished = true;if (_backEventTriggered) {_lastedPageFinishedTime = DateTime.now();} else {_lastedPageFinishedTime = null;}},onWebResourceError: (WebResourceError error) {},onNavigationRequest: (NavigationRequest request) async {debugPrint('WebPage onNavigationRequest ${request.url}');debugPrint('WebPage onNavigationRequest isMainFrame ${request.isMainFrame}');//轮询,因为onNavigationRequest可能跑在onPageFinished前面,强制等待while (!_pageIsFinished) {await Future.delayed(Duration(milliseconds: 10));}if (_shouldApplyNavLockout()) {goBack(); //执行第二次backreturn NavigationDecision.prevent;}return NavigationDecision.navigate;},onUrlChange: (UrlChange change) {print('WebPage onUrlChange ${change.url}');}),)..addJavaScriptChannel('destoryCurrentPage', onMessageReceived: (JavaScriptMessage message) {//h5自己的返回键,返回到最后一步,当前页面出栈debugPrint('====destoryCurrentPage====');Nav.pop();})..addJavaScriptChannel('toNewWebPage', onMessageReceived: (JavaScriptMessage message) {//允许h5页面打开新的third_web_pageNav.push(routerName: RouterPathModuleCommon.WebPageContainer, arguments: {'url': message.message});})..addJavaScriptChannel('toLogin', onMessageReceived: (JavaScriptMessage message) {//login:有些h5页面跳转后需要登录的  logout:可能存在的h5页面提供登出功能Nav.push(routerName: RouterPathModuleAccount.LoginPage, arguments: {'url': message.message});})..addJavaScriptChannel('getStatusBarHeight', onMessageReceived: (JavaScriptMessage message) {double statusBarHeight = MediaQuery.of(context).padding.top;controller.runJavaScriptReturningResult("onStatusBarHeightReceived('$statusBarHeight')").then((value) => print("发送statusBarHeight成功"));});controller.loadRequest(Uri.parse(url));}// 判断重定向的条件: 最近一次pageFinished和navigationRequest小于xxx毫秒。 这只是个workaround,并不是十全十美的方案bool _shouldApplyNavLockout() {final timestamp = _lastedPageFinishedTime;_lastedPageFinishedTime = null;// TODO make the threshold time configurable.if (timestamp != null) {debugPrint('WebPage diff timestamp ${DateTime.now().difference(timestamp!)}');}return timestamp != null && DateTime.now().difference(timestamp) < const Duration(milliseconds: 150);}void goBack() async {if (await controller.canGoBack()) {_backEventTriggered = true;controller.goBack();} else {Navigator.pop(context);}}@overrideWidget build(BuildContext context) {return Scaffold(body: WillPopScope(onWillPop: () async {goBack();return false;},child: WebViewWidget(controller: controller),),);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes集群操作

查看集群信息&#xff1a; kubectl get nodes 删除节点 &#xff08;⽆效且显示的也可以删除&#xff09; 后期如果 要删除某个节点&#xff0c;为了不增加其他节点的访问压力&#xff0c;先增加一个节点&#xff0c;再删除要删除的节点 语法 &#xff1a;kubect delete…

【C++】从零到一掌握红黑树:数据结构中的平衡之道

个人主页: 起名字真南的CSDN博客 个人专栏: 【数据结构初阶】 &#x1f4d8; 基础数据结构【C语言】 &#x1f4bb; C语言编程技巧【C】 &#x1f680; 进阶C【OJ题解】 &#x1f4dd; 题解精讲 目录 前言1 红黑树的概念**红黑树的五大性质** 2 红黑树的实现2.1 红黑树的结构…

支持ACME协议可免费申请SSL证书的多种渠道

目录 一、ACME 协议 二、ACMESSL可视化 三、ACME证书申请流程 三、ACME申请提交 四、使用 ACME 的好处 五、ACME总结 一、ACME 协议 ACME 协议是一种开放标准&#xff0c;旨在自动执行数字证书颁发和续订流程&#xff0c;它彻底改变了证书管理。ACME 的开发旨在简化整个…

Socket编程(TCP/UDP详解)

前言&#xff1a;之前因为做项目和找实习没得空&#xff0c;计算机网络模块并没有写成博客&#xff0c;最近得闲了&#xff0c;把计算机网络模块博客补上。 目录 一&#xff0c;UDP编程 1&#xff09;创建套接字 2&#xff09;绑定端口号 3&#xff09;发送与接收数据 4&…

【人工智能-科普】图神经网络(GNN):与传统神经网络的区别与优势

文章目录 图神经网络(GNN):与传统神经网络的区别与优势什么是图神经网络?图的基本概念GNN的工作原理GNN与传统神经网络的不同1. 数据结构的不同2. 信息传递方式的不同3. 模型的可扩展性4. 局部与全局信息的结合GNN的应用领域总结图神经网络(GNN):与传统神经网络的区别与…

【知识分享】离散行业有哪些?

【大家好&#xff0c;我是唐Sun&#xff0c;唐Sun的唐&#xff0c;唐Sun的Sun。】 离散行业是指生产过程由一系列独立、非连续的工序或环节组成&#xff0c;产品形态和性质发生变化的行业&#xff0c;常见的离散行业有以下几类&#xff1a; 装备制造行业 包括通用装备制造、专…

【Spark源码分析】规则框架- `analysis`分析阶段使用的规则

analysis分析阶段使用的规则 规则批策略规则说明SubstitutionfixedPointOptimizeUpdateFields该规则优化了 UpdateFields 表达式链&#xff0c;因此看起来更像优化规则。但是&#xff0c;在处理深嵌套模式时&#xff0c;UpdateFields 表达式树可能会非常复杂&#xff0c;导致分…

Qt详解QUiLoader 动态加载UI文件

文章目录 详解 QUiLoader 模块的使用1. QUiLoader 简介1.1 应用场景 2. 准备工作2.1 添加模块依赖2.2 引入头文件 3. 使用 QUiLoader 加载界面3.1 示例代码form.uimain.cpp 4. 常用方法详解4.1 load函数原型作用参数返回值示例代码 4.2 createWidget函数原型作用参数返回值示例…

【Git 工具】用 IntelliJ IDEA 玩转 Git 分支与版本管理

文章目录 一、使用 IDEA 配置和操作 Git1.1 查看 Idea 中的 Git 配置1.2 克隆 Github 项目到本地 二、版本管理2.1 提交并推送修改2.2 拉取远程仓库2.3 查看历史2.4 版本回退 三、分支管理3.1 新建分支3.2 切换分支3.2 合并分支3.4 Cherry-Pick 参考资料 一、使用 IDEA 配置和操…

shell编程(4)脚本与用户交互以及if条件判断

声明&#xff1a; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

架构-微服务-服务调用Dubbo

文章目录 前言一、Dubbo介绍1. 什么是Dubbo 二、实现1. 提供统一业务api2. 提供服务提供者3. 提供服务消费者 前言 服务调用方案--Dubbo‌ 基于 Java 的高性能 RPC分布式服务框架&#xff0c;致力于提供高性能和透明化的 RPC远程服务调用方案&#xff0c;以及SOA服务治理方案。…

【Python网络爬虫笔记】2-HTTP协议中网络爬虫需要的请求头和响应头内容

1 HTTP 协议整理 HTTP&#xff08;Hyper Text Transfer Protocol&#xff09;即超文本传输协议&#xff0c;是用于从万维网&#xff08;WWW&#xff09;服务器传输超文本到本地浏览器的传送协议&#xff0c;直白点儿&#xff0c;就是浏览器和服务器之间的数据交互就是通过 HTT…

【前端开发】小程序无感登录验证

概述 封装的网络请求库&#xff0c;主要用于处理 API 请求并支持自动处理 token 过期 和 token 刷新&#xff0c;适用于需要身份验证的应用场景&#xff0c;特别是在移动端中。 主要功能 自动附加 Token 在每个请求中自动附加 Authorization 头部&#xff0c;使用存储的 acces…

关于Spring基础了解

Spring简介 Spring框架是一个开源的Java应用框架&#xff0c;旨在简化企业级应用程序的开发。它提供了一系列强大的工具和服务&#xff0c;帮助开发者构建高质量的Java应用程序。Spring框架的核心理念是使开发过程更加模块化、可测试和可维护。 主要特性 依赖注入&#xff08…

解析 SpringBoot 新冠密接者跟踪系统:灵活的权限管理机制

第2章 程序开发技术 2.1 Mysql数据库 为了更容易理解Mysql数据库&#xff0c;接下来就对其具备的主要特征进行描述。 &#xff08;1&#xff09;首选Mysql数据库也是为了节省开发资金&#xff0c;因为网络上对Mysql的源码都已进行了公开展示&#xff0c;开发者根据程序开发需要…

TYUT设计模式大题

对比简单工厂&#xff0c;工厂方法&#xff0c;抽象工厂模式 比较安全组合模式和透明组合模式 安全组合模式容器节点有管理子部件的方法&#xff0c;而叶子节点没有&#xff0c;防止在用户在叶子节点上调用不适当的方法&#xff0c;保证了的安全性&#xff0c;防止叶子节点暴露…

SpringBoot集成Kafka和avro和Schema注册表

Schema注册表 为了提升kafka的性能&#xff0c;减少网络传输和存储的数据大小&#xff0c;可以把数据的schema部分单独存储到外部的schema注册表中&#xff0c;整体架构如下图所示&#xff1a; 1&#xff09;把所有数据需要用到的 schema 保存在注册表里&#xff0c;然后在记…

Nodemailer使用教程:在Node.js中发送电子邮件

目录 1. 简介 2. 安装 3. 基本配置 3.1 创建传输器 3.2 配置说明 4. 发送邮件 4.1 基本发送示例 4.2 发送验证码示例 5. 常见问题解决 5.1 "Greeting never received" 错误 5.2 安全建议 SMTP与邮件加密协议详解 1. SMTP简介 1.1 基本特点 2. 加密协…

Cause: java.sql.SQLException: No value specified for parameter 4

问题 执行更新sql时报错&#xff0c;异常栈如下 org.springframework.jdbc.BadSqlGrammarException: ### Error updating database. Cause: java.sql.SQLException: No value specified for parameter 4 ### The error may exist in com/my/mapper/MyMapper.java (best gue…

Wireshark 4.4.2:安全更新、错误修复、更新协议支持

流行的网络协议分析器Wireshark已更新至4.4.2版本。它可用于网络故障排除、分析、开发和教育。 已修复以下漏洞&#xff1a; wnpa-sec-2024-14 FiveCo RAP 解剖器无限循环。wnpa-sec-2024-15 ECMP 解析器崩溃。 更新的协议支持&#xff1a; ARTNET、ASN.1 PER、BACapp、B…