flutter开发实战-Webview及dispose关闭背景音

flutter开发实战-Webview及dispose关闭背景音

当在使用webview的时候,dispose需要关闭网页的背景音或者音效。
在这里插入图片描述

一、webview的使用

在工程的pubspec.yaml中引入插件

  webview_flutter: ^4.4.2webview_cookie_manager: ^2.0.6

Webview的使用代码如下

初始化WebViewController

controller = WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted)..setBackgroundColor(const Color(0x00000000))..setNavigationDelegate(NavigationDelegate(onProgress: (int progress) {// Update loading bar.},onPageStarted: (String url) {},onPageFinished: (String url) {},onHttpError: (HttpResponseError error) {},onWebResourceError: (WebResourceError error) {},onNavigationRequest: (NavigationRequest request) {if (request.url.startsWith('https://www.youtube.com/')) {return NavigationDecision.prevent;}return NavigationDecision.navigate;},),)..loadRequest(Uri.parse('https://flutter.dev'));

将WebViewController传递给WebViewWidget

@override
Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Flutter Simple Example')),body: WebViewWidget(controller: controller),);
}

二、为了方便使用webview,进行封装成一个独立的widget

为了方便使用webview,进行封装成一个独立的widget

WebAppBar

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';/// 自定义Appbar
class WebAppBar extends StatefulWidget implements PreferredSizeWidget {const WebAppBar({Key? key,required this.toolbarHeight,this.elevation,this.backgroundColor,this.leadingWidget,this.trailingWidget,this.centerWidget,this.brightness,this.backgroundImageName}): super(key: key);final double toolbarHeight;final double? elevation;final Color? backgroundColor;final Widget? leadingWidget;final Widget? trailingWidget;final Widget? centerWidget;final Brightness? brightness;final String? backgroundImageName;@override// TODO: implement preferredSizeSize get preferredSize => Size(ScreenUtil().screenWidth, toolbarHeight + ScreenUtil().statusBarHeight);@overrideState<StatefulWidget> createState() => _WebAppBarState();
}class _WebAppBarState extends State<WebAppBar> {@overrideWidget build(BuildContext context) {final SystemUiOverlayStyle overlayStyle =widget.brightness == Brightness.dark? SystemUiOverlayStyle.light: SystemUiOverlayStyle.dark;Widget leadingWidget = (widget.leadingWidget ?? Container());Widget centerWidget = (widget.centerWidget ?? Container());Widget trailingWidget = (widget.trailingWidget ?? Container());return AnnotatedRegion<SystemUiOverlayStyle>(//套AnnotatedRegion是为了增加状态栏控制value: overlayStyle,child: Material(color: Colors.transparent,//套Material是为了增加elevationelevation: widget.elevation ?? 0,child: Container(padding: EdgeInsets.symmetric(horizontal: 0.0),height: widget.toolbarHeight + ScreenUtil().statusBarHeight,decoration: BoxDecoration(color: widget.backgroundColor,),child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Container(height: ScreenUtil().statusBarHeight,),Expanded(child: Container(height: widget.toolbarHeight,alignment: Alignment.center,child: Row(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Container(height: widget.toolbarHeight,child: leadingWidget,),Expanded(child: Container(alignment: Alignment.center,height: widget.toolbarHeight,child: centerWidget,),),Container(height: widget.toolbarHeight,child: trailingWidget,),],),),)],),),),);}
}

使用webview的Widget

import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';// #docregion platform_imports
// Import for Android features.
import 'package:webview_flutter_android/webview_flutter_android.dart';// Import for iOS features.
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
// #enddocregion platform_importsclass WebViewSkeleton extends StatefulWidget {const WebViewSkeleton({Key? key,required this.url,required this.onWebProgress,required this.onWebResourceError,required this.onLoadFinished,this.onWebTitleLoaded,required this.onWebViewCreated,this.appUserAgent,this.webViewUserAgent,}) : super(key: key);final String url;final String? appUserAgent;final String? webViewUserAgent;final Function(int progress) onWebProgress;final Function(WebResourceError error) onWebResourceError;final Function(String? url) onLoadFinished;final Function(String? webTitle)? onWebTitleLoaded;final Function(WebViewController controller) onWebViewCreated;@overrideState<WebViewSkeleton> createState() => _WebViewSkeletonState();
}class _WebViewSkeletonState extends State<WebViewSkeleton> {// WebViewControllerlate final WebViewController _webController;// 尝试3次,每次间隔2秒int _loadTitleTimes = 0;bool _isDisposed = false;@overridevoid initState() {// TODO: implement initStatesuper.initState();_isDisposed = false;initWebController();}void initWebController() {// #docregion platform_featureslate final PlatformWebViewControllerCreationParams params;if (WebViewPlatform.instance is WebKitWebViewPlatform) {params = WebKitWebViewControllerCreationParams(allowsInlineMediaPlayback: true,mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},);} else {params = const PlatformWebViewControllerCreationParams();}final WebViewController controller =WebViewController.fromPlatformCreationParams(params);// #enddocregion platform_featurescontroller..setJavaScriptMode(JavaScriptMode.unrestricted)..setBackgroundColor(const Color(0x00000000))..setUserAgent(widget.webViewUserAgent)..setNavigationDelegate(NavigationDelegate(onProgress: (int progress) {debugPrint('WebView is loading (progress : $progress%)');widget.onWebProgress(progress);},onPageStarted: (String url) {debugPrint('Page started loading: $url');// 网页开始加载webPageLoadedStart();print('onPageStarted url: $url');},onPageFinished: (String url) {debugPrint('Page finished loading: $url');// 网页加载完成print('onPageFinished url: $url');// 加载完成widget.onLoadFinished(url);// 获取网页的标题getWebPageTitle(url: url);},onWebResourceError: (WebResourceError error) {debugPrint('''
Page resource error:code: ${error.errorCode}description: ${error.description}errorType: ${error.errorType}isForMainFrame: ${error.isForMainFrame}''');print("onWebResourceError:${error}");widget.onWebResourceError(error);},onNavigationRequest: (NavigationRequest request) {String url = Uri.decodeComponent(request.url);bool canNavigate = false;if (url.startsWith("http")) {canNavigate = true;}// 允许路由替换return canNavigate? NavigationDecision.navigate: NavigationDecision.prevent;},onUrlChange: (UrlChange change) {debugPrint('url change to ${change.url}');},// onHttpAuthRequest: (HttpAuthRequest request) {//   openDialog(request);// },),);// #docregion platform_featuresif (controller.platform is AndroidWebViewController) {AndroidWebViewController.enableDebugging(true);(controller.platform as AndroidWebViewController).setMediaPlaybackRequiresUserGesture(false);}// #enddocregion platform_features_webController = controller;onWebViewCreated();}void onWebViewCreated() {print("onWebViewCreated");// controller.loadUrl(url);此时也可以初始化一个url_webController.canGoBack().then((res) {// 是否能返回上一级print("controller.canGoBack res: $res");});_webController.currentUrl().then((url) {// 返回当前urlprint("controller.currentUrl url: $url");});_webController.canGoForward().then((res) {//是否能前进print("controller.canGoForward res: $res");});String filePre = "file://";if (widget.url.startsWith(filePre)) {String html = widget.url.substring(filePre.length);DefaultAssetBundle.of(context).loadString('assets/htmls/${html}').then((value) => _webController?.loadHtmlString(value));} else {if (widget.url.startsWith("http://") ||widget.url.startsWith("https://")) {_webController.loadRequest(Uri.parse(widget.url), headers: {'Referer': widget.url,});}}widget.onWebViewCreated(_webController);}@overridevoid dispose() {// TODO: implement disposeprint("_WebViewSkeletonState dispose");_isDisposed = true;webControllerDispose();super.dispose();}Future<void> webControllerDispose() async {/// dispose打开空白页面,关闭音频String url = "about:blank";await _webController?.loadRequest(Uri.parse(url), headers: {});_webController?.clearCache();_webController?.clearLocalStorage();}void webPageLoadedStart() {_loadTitleTimes = 0;}Future<void> getWebPageTitle({required String url}) async {if (_isDisposed) {return;}String? title = await _webController?.getTitle();print("getWebPageTitle:${title}");if (title != null && title.isNotEmpty) {print("webTitle a:${title}");setWebPageTitle(title);} else {try {var result = await _webController?.runJavaScriptReturningResult('window.document.title');print("webTitle document.url:${result}");if (result != null && (result is String) && result.isNotEmpty) {setWebPageTitle(result);} else {result = await _webController?.runJavaScriptReturningResult('window.document.getElementsByTagName("title")[0]');print("webTitle document.getElementsByTagName:${result}");setWebPageTitle(result);}} catch (e) {print("getWebPageTitle:${e.toString()}");// 最多尝试三次if (_loadTitleTimes < 3) {Future.delayed(Duration(seconds: 2), () {_loadTitleTimes++;getWebPageTitle(url: url);});}}}}// 设置页面标题void setWebPageTitle(data) {if (widget.onWebTitleLoaded != null) {widget.onWebTitleLoaded!(data);}}// 返回void goBack() {_webController?.canGoBack().then((res) {// 是否能返回上一级print("controller.canGoBack res: $res");if (true == res) {_webController?.goBack();}});}// 刷新void reload() {_webController?.reload();}@overrideWidget build(BuildContext context) {return buildWebView(context);}Widget buildWebView(BuildContext context) {return WebViewWidget(controller: _webController,);}
}

使用web view的页面webviewPage

class WebViewPage extends StatefulWidget {const WebViewPage({Key? key,this.arguments,}) : super(key: key);final Object? arguments;@overrideState<WebViewPage> createState() => _WebViewPageState();
}class _WebViewPageState extends State<WebViewPage> {String title = "";String? url;// WebViewControllerWebViewController? _webViewController;double webProgress = 0.0;String? webViewUserAgent;String? appUserAgent;String? webTitle;@overridevoid initState() {// TODO: implement initStateif (widget.arguments != null && widget.arguments is Map) {Map obj = widget.arguments as Map;url = obj["url"];webViewUserAgent = obj['webViewUserAgent'];appUserAgent = obj['appUserAgent'];webTitle = obj['webTitle'];}loggerInfo("_WebViewPageState arguments:${widget.arguments}");loggerInfo("_WebViewPageState url:${url}");super.initState();}@overridevoid dispose() {// TODO: implement disposesuper.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: WebAppBar(toolbarHeight: 44.0,backgroundColor: Theme.of(context).primaryColor,centerWidget: Text(webTitle ?? title,textAlign: TextAlign.center,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 17,color: ColorUtil.hexColor(0xffffff),fontWeight: FontWeight.w600,fontStyle: FontStyle.normal,decoration: TextDecoration.none,),),leadingWidget: Row(mainAxisAlignment: MainAxisAlignment.start,crossAxisAlignment: CrossAxisAlignment.center,children: [IconButton(padding: EdgeInsets.all(0.0),onPressed: () {webViewGoBack(context);},icon: Icon(Icons.arrow_back_ios,color: Colors.white,size: 24.0,),),IconButton(padding: EdgeInsets.all(0.0),onPressed: () {navigatorBack(context);},icon: Icon(Icons.close_rounded,color: Colors.white,size: 30.0,),),],),trailingWidget: Row(mainAxisAlignment: MainAxisAlignment.end,crossAxisAlignment: CrossAxisAlignment.center,children: [SizedBox(width: 28.0,),IconButton(padding: EdgeInsets.all(0.0),onPressed: () {webViewReload();},icon: Icon(Icons.refresh_outlined,color: Colors.white,size: 28.0,),),],),),body: Stack(children: [WebViewSkeleton(url: url ?? "",onWebResourceError: (WebResourceError error) {if (mounted) {// TODO onWebResourceError}},onWebProgress: (int progress) {if (mounted) {// TODO onWebProgressdouble precent = progress / 100.0;if (precent > 1.0) {precent = 1.0;}if (precent < 0.0) {precent = 0.0;}setState(() {webProgress = precent;loggerInfo("webProgress:${webProgress}");});}},onLoadFinished: (String? url) {if (mounted) {// TODO onLoadFinished}},onWebTitleLoaded: (String? webTitle) {if (mounted) {String? aWebTitle;if ('""' != webTitle) {aWebTitle = webTitle;}setState(() {title = aWebTitle ?? "";});}},onWebViewCreated: (WebViewController controller) {_webViewController = controller;},),buildProgressIndicator(context),],),);}Widget buildProgressIndicator(BuildContext context) {return (webProgress != 1.0)? LinearProgressIndicator(backgroundColor: Colors.transparent,valueColor: AlwaysStoppedAnimation(ColorUtil.hexColor(0x3b93ff)),value: webProgress,minHeight: 2,): Container();}void navigatorBack(BuildContext context) {Navigator.of(context).pop();}void webViewGoBack(BuildContext context) {_webViewController?.canGoBack().then((res) {// 是否能返回上一级loggerInfo("controller.canGoBack res: $res");if (true == res) {_webViewController?.goBack();} else {navigatorBack(context);}});}void webViewReload() {_webViewController?.reload();}
}

三、解决dispose关闭背景音乐

解决dispose关闭背景音乐的问题,当widget被dispose的时候,我们可以通过加载一个空白页面,来实现这个关闭背景音乐。
加载空白

代码如下

@overridevoid dispose() {// TODO: implement disposeprint("_WebViewSkeletonState dispose");_isDisposed = true;webControllerDispose();super.dispose();}Future<void> webControllerDispose() async {/// dispose打开空白页面,关闭音频String url = "about:blank";await _webController?.loadRequest(Uri.parse(url), headers: {});_webController?.clearCache();_webController?.clearLocalStorage();}

四、小结

flutter开发实战-Webview及dispose关闭背景音

学习记录,每天不停进步。

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

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

相关文章

uni-app上传失败超出文件限制解决方法-分包处理-预加载

分包背景 当你的上传出现一下错误&#xff1a; Error: 系统错误&#xff0c;错误码&#xff1a;80051,source size 2089KB exceed max limit 2MB [20240703 10:53:06][wxbf93dfb6cb3eb8af] [1.06.2405010][win32-x64] 说明你主包太大需要处理了&#xff0c;一下两种方法可以…

Day03-Jenkins与集成案例

Day03-Jenkins与集成案例 6. CD持续交付&#xff0c;持续部署实现方案7. 案例04: basketball案例,搭建开发测试专用的任务7.1 任务要求7.2 步骤7.3 详细步骤1&#xff09;安装插件2&#xff09;创建任务 7.4 gitlab配置钩子1) 解除钩子局域网访问限制2) gitlab配置钩子 7.5 与部…

网络基础:OSPF 协议

OSPF&#xff08;Open Shortest Path First&#xff09;是一种广泛使用的链路状态路由协议&#xff0c;用于IP网络中的内部网关协议&#xff08;IGP&#xff09;。OSPF通过在网络中的所有路由器之间交换路由信息&#xff0c;选择从源到目的地的最优路径。OSPF工作在OSI模型的第…

Nginx系列(二)---Mac上的快速使用

一、安装 前置软件&#xff1a;Homebrew 安装方法&#xff1a;终端输入/bin/bash -c "$(curl -fsSL <https://cdn.jsdelivr.net/gh/ineo6/homebrew-install/install.sh>)"更新&#xff1a; brew update 设置中科大镜像源&#xff1a;git -C "$(brew --r…

昇思25天学习打卡营第10天 | 自然语言处理:RNN实现情感分类

1. RNN实现情感分类 1.2 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative(负面) 预测标签: Negative输…

初识Spark

一、简介 官网&#xff1a;Apache Spark™ - Unified Engine for large-scale data analytics Apache的顶级项目&#xff0c;用于大规模数据处理的统一分析引擎。 支持语言&#xff1a;Java、Scala、Python和R (源码为Scala) 高级工具&#xff1a; 1、SparkSQL用于SQL和结构…

Linux中cat命令的英文含义

我之前一直在想cat不是猫的意思吗&#xff0c;但是cat命令在Linux中并不是指"猫"这个动物&#xff0c;而是来源于它的功能&#xff1a;concatenate&#xff08;连接&#xff09;和typeset&#xff08;打印&#xff09;。这个命令的名称是这两个功能的首字母缩写。尽管…

DevExpress WPF中文教程:Grid - 如何显示摘要(设计时)?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

Mac/Linux安装JMeter压测工具

Mac安装JMeter压测工具 介绍 Apache JMeter™应用程序是开源软件&#xff0c;是一个100%纯的Java应用程序&#xff0c;旨在加载测试功能行为和衡量性能。它最初是为测试Web应用程序而设计的&#xff0c;但后来扩展到其他测试功能。 我能用它做什么&#xff1f; Apache JMet…

vue高德地图使用

先根据官方方法给vue项目引入高德 高德文档地址 做好准备后使用 初始化地图 AMap.plugin(AMap.MoveAnimation, () >{//地图this.map new AMap.Map("mapContainer", {resizeEnable: true,center: [116.397447,39.909176],//地图中心坐标zoom:12,//缩放值});this.…

Appium+python自动化(三十九)-Appium自动化测试框架综合实践 - 代码实现(超详解)

1.简介 今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现。由于时间的关系&#xff0c;宏哥这里用代码给小伙伴演示两个模块&#xff1a;注册和登录。 2.业务模块封装 因为现在各种APP的层出不群&#xff0c;各式各样的。但是其大多数都有注册、登录。为…

走在健康前沿:低GI食品认证与现代饮食的新篇章

随着现代社会节奏的加快&#xff0c;人们对健康饮食的追求也日益增强。在众多饮食理念中&#xff0c;低血糖生成指数&#xff08;GI&#xff09;食品凭借其对控制血糖和预防慢性疾病的潜在益处&#xff0c;逐渐成为健康饮食领域的明星。 GI的科学解码 GI&#xff0c;即食物血糖…

CTFHUB-SSRF-URL Bypass

开启题目 给出提示&#xff0c;url参数的值中必须包含有 http://notfound.ctfhub.com &#xff0c;可以采用&#xff0c;也就是 HTTP 基本身份认证绕过 HTTP 基本身份认证允许 Web 浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式。 也就是…

AIGC文生图lora微调训练案例;SD-Train界面训练stable Diffusion lora模型

lora仓库&#xff08;1000多个lora微调模型分享&#xff09;&#xff1a; https://lorastudio.co/models 1、命令代码方式&#xff1a;文生图lora微调训练案例 主要用huggingface相关包&#xff1a;peft、accelerate、diffusers 参考&#xff1a; https://huggingface.co/blo…

Swift 6:导入语句上的访问级别

文章目录 前言示例启用 AccessLevelOnImport破坏性变更采用这些更改总结前言 SE-0409 提案引入了一项新功能,即允许使用 Swift 的任何可用访问级别标记导入声明,以限制导入的符号可以在哪些类型或接口中使用。由于这些变化,现在可以将依赖项标记为对当前源文件(private 或…

PostgreSQL的pg_filedump工具

PostgreSQL的pg_filedump工具 基础信息 OS版本&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a;16.2 pg软件目录&#xff1a;/home/pg16/soft pg数据目录&#xff1a;/home/pg16/data 端口&#xff1a;5777pg_filedump 是一个工具&#x…

JAVA小知识30:JAVA多线程篇1,认识多线程与线程安全问题以及解决方案。(万字解析)

来 多线程&#xff0c;一个学起来挺难但是实际应用不难的一个知识点&#xff0c;甚至在很多情况下都不需要考虑&#xff0c;最多就是写测试类的时候模拟一下并发&#xff0c;现在我们就来讲讲基础的多线程知识。 一、线程和进程、并发与并行 1.1、线程和进程 线程&am…

Java学习十二—Java8特性之Optional类

一、简介 Java 8 引入了 Optional​ 类作为一种容器&#xff0c;可以用来显式地表示一个值存在或不存在。它解决了传统上可能会遇到的空指针异常问题&#xff0c;同时提供了一种更优雅的方式来处理可能为null的情况。 Java 8 中引入 Optional​ 类的背景可以从以下几个方面来理…

线程池概念的详解

前言&#x1f440;~ 上一章我们介绍了什么是定时器以及如何去实现一个定时器&#xff0c;今天我们来讲解在多线程中同样很重要的一个内容线程池 线程池的出现 线程池概念 标准库中的线程池 工厂模式 newCacheThreadPool方法 newFixedThreadPool方法 ThreadPoolExecutor…

Akamai+Noname强强联合 | API安全再加强

最近&#xff0c;Akamai正式完成了对Noname Security的收购。本文我们将向大家介绍&#xff0c;经过本次收购后&#xff0c;Akamai在保护API安全性方面的后续计划和未来愿景。 Noname Security是市场上领先的API安全供应商之一&#xff0c;此次收购将让Akamai能更好地满足日益增…