当前位置: 首页 > article >正文

Flutter WebView 第三方库 内嵌 H5 页面的鸿蒙化适配与实战指南

Flutter WebView 内嵌 H5 页面的鸿蒙化适配与实战指南欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net各位小伙伴们好呀 我是那个上海某高校的大一计算机学生继续来给大家分享 Flutter for OpenHarmony 开发的学习心得今天要聊的是WebView 内嵌 H5 页面很多 App 都会内嵌一些 Web 页面比如 隐私政策、用户协议 H5 游戏、活动页面 新闻资讯用 Flutter 原生写这些页面太麻烦了直接用 WebView 嵌入现成的网页不香吗今天就来详细分享一下一、功能引入介绍 1.1 什么时候用 WebView 隐私政策、用户协议更新频繁适合 H5 活动页面、促销专题营销需求快速上线 H5 小游戏无需原生开发 新闻详情、内容页内容管理系统产出1.2 Flutter WebView 方案插件优点缺点webview_flutter官方维护、功能完善 ✅配置稍复杂flutter_webview_pluginAPI 简单已停止维护我们选择webview_flutter二、环境与依赖配置 2.1 pubspec.yaml 依赖dependencies:flutter:sdk:flutter# WebView webview_flutter:^4.10.0三、分步实现完整代码 3.1 基础 WebView 页面importpackage:flutter/material.dart;importpackage:webview_flutter/webview_flutter.dart;/// WebView 页面////// 用于内嵌 H5 页面支持加载进度显示、导航控制等classWebViewPageextendsStatefulWidget{/// 网页 URLfinalStringurl;/// 页面标题可选finalString?title;/// 是否显示 AppBarfinalbool showAppBar;constWebViewPage({super.key,requiredthis.url,this.title,this.showAppBartrue,});overrideStateWebViewPagecreateState()_WebViewPageState();}class_WebViewPageStateextendsStateWebViewPage{/// WebView 控制器latefinalWebViewController_controller;/// 是否正在加载bool _isLoadingtrue;/// 加载进度0.0 - 1.0double _loadingProgress0;overridevoidinitState(){super.initState();_initWebView();}/// 初始化 WebViewvoid_initWebView(){_controllerWebViewController()// 允许 JavaScript..setJavaScriptMode(JavaScriptMode.unrestricted)// 设置背景色..setBackgroundColor(Colors.white)// 设置导航代理..setNavigationDelegate(NavigationDelegate(// 加载进度回调onProgress:(int progress){setState((){_loadingProgressprogress/100;});},// 页面开始加载onPageStarted:(Stringurl){setState(()_isLoadingtrue);},// 页面加载完成onPageFinished:(Stringurl){setState(()_isLoadingfalse);},// 加载错误onWebResourceError:(WebResourceErrorerror){debugPrint(WebView Error:${error.description});},// 导航请求拦截onNavigationRequest:(NavigationRequestrequest){// 只允许 http/https 链接if(request.url.startsWith(https://)||request.url.startsWith(http://)){returnNavigationDecision.navigate;}returnNavigationDecision.prevent;},),)// 加载网页..loadRequest(Uri.parse(widget.url));}/// 处理返回按钮Futurebool_onWillPop()async{// 如果可以返回上一页if(await_controller.canGoBack()){_controller.goBack();returnfalse;}returntrue;}overrideWidgetbuild(BuildContextcontext){returnPopScope(canPop:false,// 禁用默认返回行为onPopInvokedWithResult:(bool didPop,dynamicresult)async{if(didPop)return;finalshouldPopawait_onWillPop();if(shouldPopcontext.mounted){Navigator.of(context).pop();}},child:Scaffold(appBar:widget.showAppBar?_buildAppBar():null,body:Column(children:[// 加载进度条if(_isLoading)LinearProgressIndicator(value:_loadingProgress,backgroundColor:Colors.grey[200],valueColor:constAlwaysStoppedAnimationColor(Color(0xFF6366F1),),),// WebView 主体Expanded(child:WebViewWidget(controller:_controller),),],),),);}PreferredSizeWidget_buildAppBar(){returnAppBar(backgroundColor:Colors.white,elevation:1,leading:IconButton(icon:constIcon(Icons.arrow_back,color:Color(0xFF1E293B)),onPressed:()async{if(await_controller.canGoBack()){_controller.goBack();}else{if(context.mounted)Navigator.pop(context);}},),title:Text(widget.title??加载中...,style:constTextStyle(color:Color(0xFF1E293B),fontSize:18,fontWeight:FontWeight.w600,),),actions:[IconButton(icon:constIcon(Icons.refresh,color:Color(0xFF1E293B)),onPressed:()_controller.reload(),),PopupMenuButtonString(icon:constIcon(Icons.more_vert,color:Color(0xFF1E293B)),onSelected:_handleMenuAction,itemBuilder:(context)[constPopupMenuItem(value:share,child:Text(分享)),constPopupMenuItem(value:open_browser,child:Text(浏览器打开)),constPopupMenuItem(value:copy_link,child:Text(复制链接)),],),],);}/// 处理菜单操作void_handleMenuAction(Stringaction){switch(action){caseshare:ScaffoldMessenger.of(context).showSnackBar(constSnackBar(content:Text(分享功能开发中...)),);break;caseopen_browser:ScaffoldMessenger.of(context).showSnackBar(constSnackBar(content:Text(浏览器打开功能开发中...)),);break;casecopy_link:// 复制链接到剪贴板ScaffoldMessenger.of(context).showSnackBar(constSnackBar(content:Text(链接已复制)),);break;}}}3.2 WebView 页面工厂为了方便复用创建一些常用的 WebView 页面/// WebView 页面工厂////// 提供常用的 WebView 页面配置classWebViewPages{// 常用页面 URLstaticconstStringprivacyPolicyhttps://example.com/privacy;staticconstStringuserAgreementhttps://example.com/terms;staticconstStringhelpCenterhttps://example.com/help;staticconstStringpromotionActivityhttps://example.com/promotion;/// 构建 WebView 页面staticWidgetbuildPage(Stringurl,{String?title}){returnWebViewPage(url:url,title:title);}/// 隐私政策页面staticWidgetprivacyPolicyPage(){returnconstWebViewPage(url:privacyPolicy,title:隐私政策,);}/// 用户协议页面staticWidgetuserAgreementPage(){returnconstWebViewPage(url:userAgreement,title:用户协议,);}/// 帮助中心页面staticWidgethelpCenterPage(){returnconstWebViewPage(url:helpCenter,title:帮助中心,);}/// 活动专区页面staticWidgetpromotionPage(){returnconstWebViewPage(url:promotionActivity,title:活动专区,);}}3.3 JavaScript 交互Flutter 和 WebView 之间可以互相调用importpackage:flutter/dartz.dart;classWebViewWithJSPageextendsStatefulWidget{finalStringurl;finalString?title;constWebViewWithJSPage({super.key,requiredthis.url,this.title,});overrideStateWebViewWithJSPagecreateState()_WebViewWithJSPageState();}class_WebViewWithJSPageStateextendsStateWebViewWithJSPage{latefinalWebViewController_controller;overridevoidinitState(){super.initState();_initController();}void_initController(){_controllerWebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted)..setNavigationDelegate(NavigationDelegate(onPageFinished:(url){// 页面加载完成后可以执行 JavaScript_controller.runJavaScript( console.log(Flutter says: WebView loaded!); );},),)..loadRequest(Uri.parse(widget.url));}/// 调用 JavaScript 方法Futurevoid_callJavaScript()async{// 调用网页中的 getUserInfo() 方法finalresultawait_controller.runJavaScriptReturningResult(getUserInfo(),);debugPrint(JavaScript 返回:$result);}/// 向网页传递数据Futurevoid_sendDataToWeb()async{// 传递 JSON 数据给网页await_controller.runJavaScript( window.flutterData { userId: 12345, token: abc123, timestamp:${DateTime.now().millisecondsSinceEpoch}}; window.onFlutterDataReceived window.onFlutterDataReceived(window.flutterData); );}overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:Text(widget.title??WebView)),body:Column(children:[// 操作按钮Padding(padding:constEdgeInsets.all(16),child:Row(children:[ElevatedButton(onPressed:_callJavaScript,child:constText(调用 JS),),constSizedBox(width:16),ElevatedButton(onPressed:_sendDataToWeb,child:constText(传数据给 JS),),],),),// WebViewExpanded(child:WebViewWidget(controller:_controller),),],),);}}3.4 在路由中使用 WebView// 在 app_router.dart 中添加GoRoute(path:/webview,name:webView,builder:(context,state){finalurlstate.uri.queryParameters[url]??https://example.com;finaltitlestate.uri.queryParameters[title];returnWebViewPage(url:url,title:title);},),// 使用示例// context.push(/webview?urlhttps://xxx.comtitle活动页面)// 或者使用工厂方法GoRoute(path:/privacy,builder:(context,state)WebViewPages.privacyPolicyPage(),),四、开发踩坑与挫折 4.1 踩坑一WebView 不显示问题描述页面一片空白什么都不显示。排查过程检查 URL 是否正确检查网络权限检查 JavaScript 是否启用解决方案_controllerWebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted)// 确保启用 JS..loadRequest(Uri.parse(widget.url));4.2 踩坑二Android 11 无法加载 HTTP问题描述Android 11 系统默认禁止加载 HTTP 链接。解决方案在android/app/src/main/AndroidManifest.xml中添加uses-permissionandroid:nameandroid.permission.INTERNET/!-- 如果需要加载 HTTP非 HTTPS添加 --applicationandroid:usesCleartextTraffictrue...4.3 踩坑三页面返回问题问题描述按返回键直接退出 App而不是返回 WebView 的上一页。解决方案使用PopScopecanGoBack()PopScope(canPop:false,onPopInvokedWithResult:(didPop,result)async{if(await_controller.canGoBack()){_controller.goBack();}elseif(context.mounted){Navigator.of(context).pop();}},child:...,)五、鸿蒙专属适配 5.1 鸿蒙设备权限配置在鸿蒙设备的module.json5中配置网络权限{module:{requestPermissions:[{name:ohos.permission.INTERNET}]}}六、最终实现效果 此处附鸿蒙设备上成功运行的截图—七、个人学习总结 通过 WebView 的学习我收获了很多✅ 学会了如何在 Flutter 中嵌入 H5 页面✅ 学会了 WebView 的各种配置✅ 学会了 Flutter 和 JavaScript 的交互作者上海某高校大一学生Flutter 爱好者发布时间2026年4月

相关文章:

Flutter WebView 第三方库 内嵌 H5 页面的鸿蒙化适配与实战指南

Flutter WebView 内嵌 H5 页面的鸿蒙化适配与实战指南欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net各位小伙伴们好呀!👋 我是那个上海某高校的大一计算机学生,继续来给大家分享 Flutter for OpenHarmon…...

Idea运行较老的eclipse项目,页面加载404,发请求失败

解决导入比较古老的eclipse java项目至idea中,可以在tomcat上编译部署,但是项目启动之后,网页404、后端接口请求也连接不上等相关问题。本问题中所用的idea为最新的25版本,jdk版本为1.8,所述步骤中的中文选项需对应成英…...

3步搞定喜马拉雅VIP音频下载:这款跨平台工具让你轻松保存付费内容

3步搞定喜马拉雅VIP音频下载:这款跨平台工具让你轻松保存付费内容 【免费下载链接】xmly-downloader-qt5 喜马拉雅FM专辑下载器. 支持VIP与付费专辑. 使用GoQt5编写(Not Qt Binding). 项目地址: https://gitcode.com/gh_mirrors/xm/xmly-downloader-qt5 还在…...

你的显卡能跑多快?实测RTX 4060/2080Ti破解RAR密码的速度与成本分析

显卡算力对决:RTX 4060与2080Ti破解RAR密码的实战效能评估 当面对一个加密的RAR压缩包时,许多技术爱好者首先想到的可能是"我的显卡需要多久才能破解这个密码?"这个问题看似简单,实则涉及硬件性能、算法效率、散热条件…...

STM32F103C6T6用GPIO模拟SPI驱动DAC8552:从CubeMX配置到完整代码的避坑指南

STM32F103C6T6用GPIO模拟SPI驱动DAC8552:从CubeMX配置到完整代码的避坑指南 当我们需要在嵌入式系统中实现高精度模拟电压输出时,内置DAC往往难以满足需求。STM32F103C6T6作为经典Cortex-M3微控制器,虽然具备12位DAC,但面对需要16…...

别再死记硬背了!用‘生命体’比喻彻底搞懂UVM的component与object

别再死记硬背了!用‘生命体’比喻彻底搞懂UVM的component与object 在芯片验证领域,UVM(Universal Verification Methodology)作为行业标准方法论,其核心架构设计常常让初学者感到困惑。特别是uvm_component与uvm_obje…...

告别Root后迷茫:手把手教你用Magisk模块激活LSPosed(Riru/Zygisk双版本保姆级教程)

从Root到模块实战:Magisk与LSPosed的终极配置指南 当你成功解锁Bootloader并完成Root后,真正的Android定制之旅才刚刚开始。面对琳琅满目的Magisk模块,特别是功能强大的LSPosed框架,许多用户会陷入选择困难——Riru还是Zygisk&am…...

华恒智信助力传统制造行业破解“干好干坏一个样”困局

又到一年考核季。当您拿到各部门上报的评分表时,是否也发现一个熟悉的现象:全员分数集中在90分以上,绩效奖金近乎平均分配,真正创造价值的人并未多得,而选择“躺平”的人也未曾少拿?更令人不安的是&#xf…...

闲鱼自动化采集系统:从零到精通的完整实战指南

闲鱼自动化采集系统:从零到精通的完整实战指南 【免费下载链接】idlefish_xianyu_spider-crawler-sender 闲鱼自动抓取/筛选/发送系统,xianyu spider crawler blablabla 项目地址: https://gitcode.com/gh_mirrors/id/idlefish_xianyu_spider-crawler-…...

别再手动巡检了!用Prometheus+vmware_exporter自动监控你的VMware vSphere集群(附K8s/Docker两种部署)

从人工巡检到智能告警:构建VMware vSphere全栈监控体系的实战指南 凌晨三点,刺耳的电话铃声划破夜空——某台关键业务虚拟机CPU负载飙升至98%,而值班工程师手忙脚乱地远程连接、收集日志、排查问题。这样的场景在传统运维模式下每周都会上演&…...

蓝桥杯嵌入式省赛真题解析:STM32G431如何用ADC+定时器实现电压计时器(附完整工程)

STM32G431实战:从零构建高精度电压计时器的5个关键步骤 在嵌入式系统开发中,ADC采集与定时器协同工作是一个经典而实用的技术组合。今天我们就以STM32G431平台为例,手把手教你构建一个工业级精度的电压阈值触发计时系统。这个方案不仅适用于蓝…...

扩散模型中的可学习方差调度

扩散模型中可学习方差调度 在扩散模型中,方差调度是控制噪声添加过程的关键组件。标准扩散模型的前向过程逐步添加噪声到数据中,其噪声方差通常由预定义的调度(如线性或余弦)控制。然而,“可学习方差调度”指的是在训…...

百度网盘Mac终极提速指南:免费解锁SVIP下载速度限制

百度网盘Mac终极提速指南:免费解锁SVIP下载速度限制 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 你是否在Mac上使用百度网盘时&#xff…...

别再混用nn.Linear和F.linear了!PyTorch中nn与nn.functional模块的实战选择指南

PyTorch中nn.Linear与F.linear的深度抉择:从原理到工程实践 在构建PyTorch神经网络时,许多开发者会困惑于何时使用nn.Linear,何时选择F.linear。这两种看似相似的线性变换实现,背后却隐藏着截然不同的设计哲学和使用场景。本文将深…...

Matlab/Simulink做AEB仿真,最让人头疼的Bus总线配置,这篇保姆级教程帮你搞定

Matlab/Simulink AEB仿真中的Bus总线配置实战指南 在自动驾驶系统开发中,自动紧急制动(AEB)算法的验证离不开高精度的仿真环境。Matlab/Simulink配合Driving Toolbox提供了强大的仿真能力,但许多工程师在实际开发中都会遇到一个共…...

BilibiliDown终极指南:跨平台B站视频下载神器完全攻略

BilibiliDown终极指南:跨平台B站视频下载神器完全攻略 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/b…...

DataHub元数据平台部署后,第一件事:手把手教你配置MySQL数据源并自动采集

DataHub元数据平台部署后第一课:MySQL数据源配置与自动化采集实战 当你完成DataHub的基础部署,看到9002端口的登录界面时,真正的挑战才刚刚开始。作为数据工程师,我们最关心的不是平台能否运行,而是如何让它快速产生业…...

C/C++新手必看:遇到‘uint32_t’未定义别慌,一分钟搞定头文件包含

C/C开发中uint32_t未定义问题的深度解析与实战指南 刚接触C/C开发的程序员在编写跨平台或嵌入式系统代码时,经常会遇到编译器报错"unknown type name uint32_t"的困扰。这个看似简单的错误背后,实际上涉及C/C标准演进、跨平台兼容性以及硬件抽…...

第21篇:预训练模型BERT实战——轻松调用NLP领域的“瑞士军刀”(项目实战)

文章目录项目背景:当“理解”成为瓶颈技术选型:为什么是BERT Hugging Face Transformers?架构设计:微调(Fine-tuning)的核心流程核心实现:四步搞定新闻分类环境准备第一步:数据加载…...

不是世界太乱,而是咱们的心缺了一套“防守准绳”

《斯多葛式人生管理罗盘》 发刊词 —— (0/24) 那天深夜快十二点了,我正站在阳台上给君子兰浇水。 手机突然震了一下。我瞄了一眼,是个老同事发来的。这哥们儿以前跟我在一个省中心项目上并肩熬过几个通宵,典型的“能扛事”的硬汉。他刚从干了十二年的大厂出来,整个部门被…...

AUTOSAR架构下,RoutineControl(0x31)服务回调函数怎么写才高效又易维护?

AUTOSAR架构下高效实现RoutineControl服务的工程实践指南 在汽车电子控制单元(ECU)开发中,诊断服务是不可或缺的重要组成部分。其中RoutineControl服务(0x31)因其灵活性和强大的功能,被广泛应用于传感器标定、内存操作、特殊工况控制等场景。本文将深入探…...

ARM A78AE实战:手把手教你配置L1 Cache的Memory Type与属性(避坑Device nGnRnE)

ARM Cortex-A78AE内存属性配置实战:从原理到避坑指南 在嵌入式系统开发中,正确配置处理器的内存属性是确保系统稳定性和性能的关键环节。作为ARM最新一代的实时处理器核心,Cortex-A78AE对内存类型(Memory Type)和属性的…...

applera1n激活锁绕过完整解决方案:三步搞定iOS 15-16.6设备解锁

applera1n激活锁绕过完整解决方案:三步搞定iOS 15-16.6设备解锁 【免费下载链接】applera1n icloud bypass for ios 15-16 项目地址: https://gitcode.com/gh_mirrors/ap/applera1n 面对二手iPhone的激活锁问题,你是否感到束手无策?ap…...

如何快速掌握XELFViewer:面向开发者的完整ELF文件分析实战指南

如何快速掌握XELFViewer:面向开发者的完整ELF文件分析实战指南 【免费下载链接】XELFViewer ELF file viewer/editor for Windows, Linux and MacOS. 项目地址: https://gitcode.com/gh_mirrors/xe/XELFViewer 你是否曾经面对Linux系统中的二进制文件感到无从…...

从一次线上事故复盘说起:我们是如何用SLI和SLO定责并改进系统稳定性的

从一次购物车故障复盘看SLI/SLO的工程实践价值 凌晨2点15分,电商平台的监控大屏突然亮起刺眼的红色——购物车下单成功率在10分钟内从99.98%暴跌至76%。值班工程师的钉钉群瞬间被用户投诉截图淹没,而更棘手的是,促销活动还有3小时就要开始。这…...

MIUI自动化任务工具:解放双手的终极小米社区助手

MIUI自动化任务工具:解放双手的终极小米社区助手 【免费下载链接】miui-auto-tasks 一个自动化完成小米社区任务的脚本 项目地址: https://gitcode.com/gh_mirrors/mi/miui-auto-tasks MIUI自动化任务工具是一款专为小米社区用户设计的智能脚本,能…...

LFM2.5-VL-1.6B效果实测:多语言图片描述与OCR文档理解案例分享

LFM2.5-VL-1.6B效果实测:多语言图片描述与OCR文档理解案例分享 1. 模型概览与核心能力 LFM2.5-VL-1.6B是Liquid AI推出的轻量级多模态模型,专为边缘设备和端侧应用优化。这个1.6B参数的视觉语言模型(1.2B语言400M视觉)在保持小巧…...

AUTOSAR唤醒校验:从事件检测到通道激活的完整流程解析

1. AUTOSAR唤醒流程概述 在汽车电子系统中,ECU(电子控制单元)的唤醒机制至关重要。想象一下你的车钥匙按下解锁按钮时,整个车载系统从休眠状态被唤醒的过程,这就是典型的唤醒场景。AUTOSAR标准为这种唤醒流程提供了一套…...

Mesa 3.0:基于模块化架构与AgentSet API的Python多智能体建模技术突破

Mesa 3.0:基于模块化架构与AgentSet API的Python多智能体建模技术突破 【免费下载链接】mesa Mesa is an open-source Python library for agent-based modeling, ideal for simulating complex systems and exploring emergent behaviors. 项目地址: https://git…...

OpenClaw从入门到应用——Agent:消息(Messages)

通过OpenClaw实现副业收入:《OpenClaw赚钱实录:从“养龙虾“到可持续变现的实践指南》 本页整合了 OpenClaw 处理入站消息、会话、队列、流式传输以及推理可见性的方式。 消息流程(高层视图) 入站消息-> 路由/绑定 -> 会…...