You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
  }
}

关键改进说明

  1. 新增缓存写入逻辑:在onLoadStop回调中调用_savePageToCache,通过_controller.getHtml()获取页面HTML内容,再用DefaultCacheManager保存到本地
  2. 完善网络切换处理:恢复联网时自动重新加载在线页面并更新缓存
  3. 添加错误处理:无缓存且离线时显示友好的提示页面,加载错误时自动尝试读取缓存
  4. 优化加载逻辑:初始化时无论网络状态如何,都优先加载对应内容(在线/缓存)

内容的提问来源于stack exchange,提问作者Noni Gopal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 15:01:01