news 2026/6/10 12:23:11

Flutter混合开发与WebView集成实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flutter混合开发与WebView集成实战

🔗实战项目:openharmonycrossplatform.csdn.net/content

📖 目录

  • 🌐 WebView集成

  • 🔗 混合通信

  • 📱 原生嵌入

  • 🎯 性能优化

🌐 一、WebView深度集成

1.1 WebView基础封装

dart

// lib/services/webview_service.dart import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter_android/webview_flutter_android.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; class WebViewService { // 创建WebViewController static Future<WebViewController> createController({ required String initialUrl, bool javascriptEnabled = true, bool autoMediaPlayback = false, }) async { late final PlatformWebViewControllerCreationParams params; if (WebViewPlatform.instance is WebKitWebViewPlatform) { params = WebKitWebViewControllerCreationParams( allowsInlineMediaPlayback: true, mediaTypesRequiringUserAction: autoMediaPlayback ? const <PlaybackMediaTypes>{} : const <PlaybackMediaTypes>{PlaybackMediaTypes.video}, ); } else { params = const PlatformWebViewControllerCreationParams(); } final controller = WebViewController.fromPlatformCreationParams(params); // 通用配置 await controller.setJavaScriptMode(JavascriptMode.unrestricted); await controller.setBackgroundColor(Colors.transparent); await controller.setNavigationDelegate(NavigationDelegate( onProgress: (int progress) { print('加载进度: $progress%'); }, onPageStarted: (String url) { print('开始加载: $url'); }, onPageFinished: (String url) { print('加载完成: $url'); }, onWebResourceError: (WebResourceError error) { print('加载错误: ${error.description}'); }, onNavigationRequest: (NavigationRequest request) { // 允许所有导航 return NavigationDecision.navigate; }, )); // 平台特定配置 if (controller.platform is AndroidWebViewController) { final androidController = controller.platform as AndroidWebViewController; await androidController.setMediaPlaybackRequiresUserGesture(false); } // 加载初始URL await controller.loadRequest(Uri.parse(initialUrl)); return controller; } // 执行JavaScript static Future<String?> evaluateJavascript( WebViewController controller, String script, ) async { try { return await controller.runJavaScriptReturningResult(script) as String?; } catch (e) { print('JS执行失败: $e'); return null; } } // 添加JavaScript通道 static void addJavascriptChannel( WebViewController controller, String channelName, void Function(JavascriptMessage) onMessageReceived, ) { controller.addJavaScriptChannel( channelName, onMessageReceived: onMessageReceived, ); } // 清除缓存 static Future<void> clearCache() async { final WebViewCookieManager cookieManager = WebViewCookieManager(); await cookieManager.clearCookies(); if (WebViewPlatform.instance is WebKitWebViewPlatform) { // iOS缓存清理 } else if (WebViewPlatform.instance is AndroidWebViewPlatform) { // Android缓存清理 } } }

1.2 自定义WebView组件

dart

// lib/ui/components/custom_webview.dart import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; class CustomWebView extends StatefulWidget { final String initialUrl; final bool showProgress; final Widget? errorWidget; final Map<String, String>? headers; const CustomWebView({ super.key, required this.initialUrl, this.showProgress = true, this.errorWidget, this.headers, }); @override State<CustomWebView> createState() => _CustomWebViewState(); } class _CustomWebViewState extends State<CustomWebView> { late final WebViewController _controller; bool _isLoading = true; bool _hasError = false; double _progress = 0; @override void initState() { super.initState(); _initWebView(); } Future<void> _initWebView() async { try { _controller = await WebViewService.createController( initialUrl: widget.initialUrl, ); // 监听加载状态 _controller.setNavigationDelegate(NavigationDelegate( onProgress: (progress) { setState(() { _progress = progress / 100; _isLoading = progress < 100; }); }, onPageStarted: (url) { setState(() { _isLoading = true; _hasError = false; }); }, onPageFinished: (url) { setState(() { _isLoading = false; }); }, onWebResourceError: (error) { setState(() { _isLoading = false; _hasError = true; }); }, )); // 添加JS通道 WebViewService.addJavascriptChannel( _controller, 'FlutterChannel', (message) { _handleJsMessage(message.message); }, ); } catch (e) { setState(() { _hasError = true; _isLoading = false; }); } } void _handleJsMessage(String message) { print('收到JS消息: $message'); // 处理JS消息 } void _reload() { _controller.reload(); setState(() { _hasError = false; _isLoading = true; }); } void _goBack() async { if (await _controller.canGoBack()) { await _controller.goBack(); } } void _goForward() async { if (await _controller.canGoForward()) { await _controller.goForward(); } } @override Widget build(BuildContext context) { if (_hasError) { return widget.errorWidget ?? _buildErrorWidget(); } return Column( children: [ // 进度条 if (widget.showProgress && _isLoading) LinearProgressIndicator(value: _progress), // 导航栏 _buildNavigationBar(), // WebView内容 Expanded( child: WebViewWidget( controller: _controller, ), ), ], ); } Widget _buildNavigationBar() { return FutureBuilder<bool>( future: Future.wait([ _controller.canGoBack(), _controller.canGoForward(), ]).then((results) => true), builder: (context, snapshot) { return Container( color: Colors.grey[100], padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( children: [ IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => _goBack(), ), IconButton( icon: const Icon(Icons.arrow_forward), onPressed: () => _goForward(), ), IconButton( icon: const Icon(Icons.refresh), onPressed: _reload, ), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( widget.initialUrl, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ), ], ), ); }, ); } Widget _buildErrorWidget() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error, size: 64, color: Colors.red), const SizedBox(height: 16), const Text('加载失败'), const SizedBox(height: 16), ElevatedButton( onPressed: _reload, child: const Text('重新加载'), ), ], ), ); } }

🔗 二、混合通信系统

2.1 Flutter与JavaScript通信

dart

// lib/services/webview_bridge.dart import 'dart:convert'; class WebViewBridge { final WebViewController controller; WebViewBridge(this.controller); // 向JS发送消息 Future<void> sendToJavaScript(String event, dynamic data) async { final message = jsonEncode({'event': event, 'data': data}); final script = ''' if (window.flutterBridge) { window.flutterBridge.receiveFromFlutter($message); } '''; try { await controller.runJavaScript(script); } catch (e) { print('发送到JS失败: $e'); } } // 从JS接收消息 void setupJavaScriptChannels() { controller.addJavaScriptChannel( 'FlutterBridge', onMessageReceived: (message) { _handleJavaScriptMessage(message.message); }, ); } void _handleJavaScriptMessage(String message) { try { final decoded = jsonDecode(message) as Map<String, dynamic>; final event = decoded['event'] as String; final data = decoded['data']; print('收到JS事件: $event'); switch (event) { case 'userLogin': _handleUserLogin(data); break; case 'paymentComplete': _handlePaymentComplete(data); break; case 'navigationRequest': _handleNavigationRequest(data); break; default: print('未知事件: $event'); } } catch (e) { print('解析JS消息失败: $e'); } } void _handleUserLogin(dynamic data) { // 处理用户登录 print('用户登录: $data'); } void _handlePaymentComplete(dynamic data) { // 处理支付完成 print('支付完成: $data'); } void _handleNavigationRequest(dynamic data) { // 处理导航请求 print('导航请求: $data'); } // 注入JS桥接代码 Future<void> injectBridgeScript() async { const bridgeScript = ''' window.flutterBridge = { sendToFlutter: function(event, data) { FlutterBridge.postMessage( JSON.stringify({event: event, data: data}) ); }, receiveFromFlutter: function(message) { // 由具体页面实现 console.log('收到Flutter消息:', message); } }; '''; await controller.runJavaScript(bridgeScript); } }

2.2 HTML模板管理

dart

// lib/services/html_template_service.dart class HtmlTemplateService { // 加载本地HTML模板 Future<String> loadLocalTemplate(String assetPath) async { return await rootBundle.loadString(assetPath); } // 创建HTML页面 String createHtmlPage({ required String title, required String content, String? style, String? script, }) { return ''' <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>$title</title> <style> body { margin: 0; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, sans-serif; } $style </style> </head> <body> $content <script> $script </script> </body> </html> '''; } // 创建数据可视化HTML String createChartHtml(Map<String, dynamic> data) { final chartData = jsonEncode(data); return createHtmlPage( title: '数据图表', content: ''' <div id="chart" style="width: 100%; height: 400px;"></div> ''', script: ''' // 使用Chart.js或其他库渲染图表 const data = $chartData; renderChart(data); ''', ); } // 创建表单HTML String createFormHtml(List<Map<String, dynamic>> fields) { final fieldsHtml = fields.map((field) { return ''' <div class="form-group"> <label>${field['label']}</label> <input type="${field['type']}" name="${field['name']}" placeholder="${field['placeholder']}"> </div> '''; }).join(); return createHtmlPage( title: '表单', content: ''' <form id="myForm"> $fieldsHtml <button type="submit">提交</button> </form> ''', script: ''' document.getElementById('myForm').addEventListener('submit', function(e) { e.preventDefault(); const formData = new FormData(this); const data = Object.fromEntries(formData); window.flutterBridge.sendToFlutter('formSubmit', data); }); ''', ); } }

📱 三、原生嵌入方案

3.1 Flutter与原生视图混合

dart

// lib/ui/hybrid/native_embedding.dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class NativeEmbeddingPage extends StatefulWidget { const NativeEmbeddingPage({super.key}); @override State<NativeEmbeddingPage> createState() => _NativeEmbeddingPageState(); } class _NativeEmbeddingPageState extends State<NativeEmbeddingPage> { static const platform = MethodChannel('com.example/native_view'); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('原生嵌入'), ), body: Column( children: [ // Flutter部分 Expanded( flex: 2, child: ListView( children: [ ListTile( title: const Text('Flutter组件'), subtitle: const Text('标准的Flutter组件'), trailing: IconButton( icon: const Icon(Icons.refresh), onPressed: _callNativeMethod, ), ), ], ), ), // 原生视图容器 Expanded( flex: 3, child: _buildNativeViewContainer(), ), ], ), ); } Widget _buildNativeViewContainer() { if (Platform.isAndroid) { return AndroidView( viewType: 'com.example/native_view', creationParams: {'text': '从Flutter传递的参数'}, creationParamsCodec: const StandardMessageCodec(), onPlatformViewCreated: _onPlatformViewCreated, ); } else if (Platform.isIOS) { return UiKitView( viewType: 'com.example/native_view', creationParams: {'text': '从Flutter传递的参数'}, creationParamsCodec: const StandardMessageCodec(), onPlatformViewCreated: _onPlatformViewCreated, ); } return const Center(child: Text('不支持此平台')); } void _onPlatformViewCreated(int id) { // 原生视图创建完成 print('原生视图ID: $id'); // 设置消息处理器 platform.setMethodCallHandler(_handleMethodCall); } Future<dynamic> _handleMethodCall(MethodCall call) async { switch (call.method) { case 'nativeEvent': print('收到原生事件: ${call.arguments}'); return '收到'; default: throw PlatformException( code: '未实现', message: '方法 ${call.method} 未实现', ); } } Future<void> _callNativeMethod() async { try { final result = await platform.invokeMethod('flutterMethod', { 'param1': 'value1', 'param2': 'value2', }); print('原生方法返回: $result'); } on PlatformException catch (e) { print('调用失败: ${e.message}'); } } }

3.2 Android原生视图实现

kotlin

// android/src/main/kotlin/com/example/NativeView.kt package com.example import android.content.Context import android.view.View import android.widget.TextView import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.platform.PlatformView class NativeView( context: Context, messenger: BinaryMessenger, viewId: Int, creationParams: Map<String, Any>? ) : PlatformView { private val textView: TextView private val methodChannel: MethodChannel init { textView = TextView(context) methodChannel = MethodChannel(messenger, "com.example/native_view_$viewId") // 设置文本 val text = creationParams?.get("text") as? String ?: "默认文本" textView.text = "原生视图: $text" // 设置点击事件 textView.setOnClickListener { methodChannel.invokeMethod("nativeEvent", mapOf( "viewId" to viewId, "event" to "click" )) } // 设置方法处理器 methodChannel.setMethodCallHandler { call, result -> when (call.method) { "updateText" -> { val newText = call.arguments as? String textView.text = newText result.success(true) } else -> result.notImplemented() } } } override fun getView(): View { return textView } override fun dispose() { methodChannel.setMethodCallHandler(null) } }

🎯 四、性能优化策略

4.1 WebView性能优化

dart

// lib/services/webview_optimizer.dart class WebViewOptimizer { // 预加载WebView static Future<WebViewController> preloadWebView({ required String url, bool enableCache = true, }) async { final controller = await WebViewService.createController( initialUrl: url, ); if (enableCache) { _configureCache(controller); } return controller; } static void _configureCache(WebViewController controller) { // 配置缓存策略 if (controller.platform is AndroidWebViewController) { final androidController = controller.platform as AndroidWebViewController; // Android缓存配置 } } // 内存管理 static void manageMemory(WebViewController controller) { // 定期清理内存 controller.clearCache(); controller.clearLocalStorage(); } // 监控性能 static Stream<WebViewPerformance> monitorPerformance( WebViewController controller, ) async* { // 监控加载时间 final stopwatch = Stopwatch()..start(); // 监听页面加载完成 controller.setNavigationDelegate(NavigationDelegate( onPageFinished: (url) { stopwatch.stop(); print('页面加载时间: ${stopwatch.elapsedMilliseconds}ms'); }, )); // 监控内存使用 // 可以通过MethodChannel获取原生内存信息 } } class WebViewPerformance { final int loadTime; final int memoryUsage; final int networkRequests; const WebViewPerformance({ required this.loadTime, required this.memoryUsage, required this.networkRequests, }); }

4.2 混合应用打包优化

dart

// lib/build/build_optimizer.dart class BuildOptimizer { // 分析包大小 static Future<void> analyzePackageSize() async { // 检查资源文件 final assetFiles = await _getAssetFiles(); final largeFiles = assetFiles.where((file) => file.size > 1024 * 1024); if (largeFiles.isNotEmpty) { print('发现大文件:'); for (final file in largeFiles) { print(' ${file.path}: ${file.size ~/ 1024}KB'); } } } // 资源文件信息 static Future<List<AssetFile>> _getAssetFiles() async { final manifest = await rootBundle.loadString('AssetManifest.json'); final manifestMap = jsonDecode(manifest) as Map<String, dynamic>; final files = <AssetFile>[]; for (final key in manifestMap.keys) { if (key.contains('assets/web/')) { // Web资源文件 final byteData = await rootBundle.load(key); files.add(AssetFile( path: key, size: byteData.lengthInBytes, )); } } return files; } // 优化建议 static List<String> getOptimizationSuggestions() { return [ '1. 压缩图片资源', '2. 移除未使用的Web资源', '3. 使用CDN加载外部资源', '4. 启用GZIP压缩', '5. 实现资源懒加载', ]; } } class AssetFile { final String path; final int size; const AssetFile({ required this.path, required this.size, }); }

📊 总结

混合开发要点:

  1. WebView集成:深度定制、性能优化

  2. 混合通信:双向消息传递、数据同步

  3. 原生嵌入:平台视图、方法通道

  4. 性能优化:缓存策略、包大小控制

依赖配置:

yaml

dependencies: webview_flutter: ^4.4.2 webview_flutter_android: ^2.11.2 webview_flutter_wkwebview: ^2.16.1

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 6:41:56

AD学习笔记-31 DRC检查

今天&#xff0c;我们介绍很重要的一部分&#xff0c;DRC检查。1、DRC找到工具-设计规则检查点开以后&#xff0c;把停止检测以后的数值修改为50000.这个意思是找到50000个就停止寻找错误&#xff0c;原来的默认值是500&#xff0c;明显可能不满足需求接着&#xff0c;我们去看…

作者头像 李华
网站建设 2026/6/9 2:28:38

AD学习笔记-32 PCB尺寸标注与边缘测量

今天&#xff0c;我们学习如何对PCB的尺寸进行标注。1、尺寸标注我们找到放置-尺寸-线性尺寸&#xff08;其他的大家自行探索&#xff09;。出现了我们的标尺&#xff0c;我们把光标放到板子的一端&#xff0c;单击。然后拖到板子的另一端&#xff0c;并把它拖出来&#xff0c;…

作者头像 李华
网站建设 2026/6/9 10:46:50

【Spring框架】SpringJDBC

Spring JDBC 与 JdbcTemplateSpring JDBC 是Spring所提供的持久层技术&#xff0c;用于简化数据库操作的一个模块&#xff0c;以一种更简洁&#xff0c;更直接的方式使用 JDBC API 简化了开发人员对数据库的操作。JdbcTemplate 则是 Spring JDBC 模块中最核心的类&#xff0c;是…

作者头像 李华
网站建设 2026/6/9 6:31:59

大模型通义千问3-VL-Plus - 视觉推理(本地图片)

一、概论 官方给出的解释&#xff1a;视觉推理模型能够先输出思考过程&#xff0c;再输出回答内容&#xff0c;适用于处理复杂的视觉分析任务&#xff0c;如解读数学题、分析图表数据或复杂视频理解等任务。 简单来说&#xff0c;视觉推理是人工智能的一个分支&#xff0c;核心…

作者头像 李华
网站建设 2026/6/7 10:09:03

测试开发面试题:浏览器输入url之后的过程

概述整体过程&#xff1a; URL解析&#xff1a;浏览器首先会解析输入的URL。URL通常由协议&#xff08;如HTTP、HTTPS&#xff09;、域名&#xff08;或IP地址&#xff09;、端口号&#xff08;如果未指定&#xff0c;默认为协议的默认端口&#xff09;、路径&#xff08;指定服…

作者头像 李华
网站建设 2026/6/10 3:50:57

她们的力量 --《人间六味》解锁职业女性的 “六味人生”

当改革开放的春风吹遍珠三角&#xff0c;一群女性以坚韧为笔、以奋斗为墨&#xff0c;《人间六味》以改革开放四十年为时代卷轴&#xff0c;聚焦汪文励、夏慧、汤曦三位女性的奋斗人生&#xff0c;用“苦辣酸甜咸淡”的六味&#xff0c;勾勒出女性在事业、婚姻与自我觉醒中逐步…

作者头像 李华