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月
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2552684.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!