Flutter WebView实现已加载网页离线访问的可行性问询
网页离线保存需求可行性分析及实现方案
你的需求完全可行,核心逻辑是「联网时加载在线页面并缓存内容,离线时读取本地缓存展示」。你提供的代码已经具备网络检测和缓存读取的基础,但缺少关键的「联网时保存页面到缓存」的步骤,下面是完善后的实现方案。
核心实现思路
- 网络状态监听:通过
connectivity_plus实时检测网络切换,触发对应的加载逻辑 - 网页内容缓存:在线页面加载完成后,将HTML内容保存到本地缓存
- 离线内容加载:无网络时读取缓存的HTML内容,通过WebView展示
完善后的代码实现
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; class MyCertificatesScreen extends StatefulWidget { static const routeName = '/webview'; final String url; const MyCertificatesScreen({Key? key, required this.url}) : super(key: key); @override _MyCertificatesScreenState createState() => _MyCertificatesScreenState(); } class _MyCertificatesScreenState extends State<MyCertificatesScreen> { late InAppWebViewController _controller; bool isLoading = true; final cacheManager = DefaultCacheManager(); @override void initState() { super.initState(); // 监听网络状态变化 Connectivity().onConnectivityChanged.listen((result) { if (result == ConnectivityResult.none) { _loadCachedHtmlFile(); } else { // 恢复联网时重新加载在线页面并更新缓存 _checkConnectivityAndLoadContent(); } }); } @override void didChangeDependencies() { super.didChangeDependencies(); _checkConnectivityAndLoadContent(); } // 加载缓存的HTML文件 void _loadCachedHtmlFile() async { try { final fileInfo = await cacheManager.getFileFromCache(widget.url); if (fileInfo != null && fileInfo.file != null && await fileInfo.file.exists()) { final fileContent = await fileInfo.file.readAsString(); _controller.loadData(data: fileContent, mimeType: 'text/html'); } else { // 无缓存时显示提示 _showOfflineError(); } } catch (e) { _showOfflineError(); } } // 检查网络并加载内容 void _checkConnectivityAndLoadContent() async { var connectivityResult = await Connectivity().checkConnectivity(); if (connectivityResult != ConnectivityResult.none) { _controller.loadUrl(urlRequest: URLRequest(url: Uri.parse(widget.url))); } else { _loadCachedHtmlFile(); } } // 保存网页内容到缓存 void _savePageToCache() async { try { final htmlContent = await _controller.getHtml(); if (htmlContent != null) { // 将HTML内容写入缓存,以URL为缓存键 await cacheManager.putFile( widget.url, htmlContent.codeUnits, fileExtension: '.html', ); } } catch (e) { print('保存缓存失败: $e'); } } // 显示离线无缓存提示 void _showOfflineError() { _controller.loadData( data: ''' <html> <body style="display:flex;align-items:center;justify-content:center;height:100vh;margin:0;"> <div style="text-align:center;"> <h2>无网络连接</h2> <p>当前没有可用网络,且未找到缓存页面</p> </div> </body> </html> ''', mimeType: 'text/html', ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F9FA), appBar: null, body: Stack( children: [ InAppWebView( initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, ), ), onWebViewCreated: (InAppWebViewController controller) { _controller = controller; _checkConnectivityAndLoadContent(); }, onLoadStop: (InAppWebViewController controller, Uri? url) { setState(() { isLoading = false; }); // 页面加载完成后保存到缓存 _savePageToCache(); }, onLoadError: (InAppWebViewController controller, Uri? url, int code, String message) { print("网页加载错误: $message"); _loadCachedHtmlFile(); }, ), if (isLoading) const Center( child: CircularProgressIndicator(), ), ], ), ); } @override void dispose() { cacheManager.dispose(); super.dispose(); } }
关键改进说明
- 新增缓存写入逻辑:在
onLoadStop回调中调用_savePageToCache,通过_controller.getHtml()获取页面HTML内容,再用DefaultCacheManager保存到本地 - 完善网络切换处理:恢复联网时自动重新加载在线页面并更新缓存
- 添加错误处理:无缓存且离线时显示友好的提示页面,加载错误时自动尝试读取缓存
- 优化加载逻辑:初始化时无论网络状态如何,都优先加载对应内容(在线/缓存)
内容的提问来源于stack exchange,提问作者Noni Gopal
相关产品推荐
相关产品推荐

