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

Flutter中如何从字节存储图片并实现缓存读取?

Flutter 图片本地缓存实现方案

核心思路

通过唯一标识生成缓存文件名,结合本地文件系统实现图片的存储与读取,请求前先检查本地缓存,存在则直接读取,不存在再发起API请求。

步骤实现

1. 添加依赖

在pubspec.yaml中引入必要的包:

dependencies:
  flutter:
    sdk: flutter
  path_provider: ^2.1.2  # 获取本地存储路径
  permission_handler: ^11.0.1  # 处理存储权限(Android/iOS)
  crypto: ^3.0.3  # 生成哈希值作为缓存键

2. 生成唯一缓存键

用图片的请求URL(或接口参数组合)生成MD5哈希值,确保每个图片对应唯一的缓存文件名:

import 'package:crypto/crypto.dart';
import 'dart:convert';

String generateCacheKey(String imageUrl) {
  var bytes = utf8.encode(imageUrl);
  var digest = md5.convert(bytes);
  return digest.toString(); // 返回32位MD5字符串作为文件名
}

3. 获取本地存储路径

选择应用专属的缓存目录(临时目录系统可能自动清理,需永久存储可改用文档目录):

import 'package:path_provider/path_provider.dart';
import 'dart:io';

Future<String> getCacheDirPath() async {
  final directory = await getTemporaryDirectory(); // 临时缓存目录
  // 永久存储用:await getApplicationDocumentsDirectory()
  return directory.path;
}

4. 缓存检查与读取

请求图片前先判断本地是否存在有效缓存文件:

Future<Uint8List?> getCachedImage(String cacheKey) async {
  final cacheDir = await getCacheDirPath();
  final file = File('$cacheDir/$cacheKey.png'); // 根据图片格式修改后缀

  if (await file.exists() && await file.length() > 0) {
    return await file.readAsBytes();
  }
  return null;
}

5. 存储图片到本地

API请求成功后,将Uint8List写入本地文件:

Future<void> saveImageToCache(String cacheKey, Uint8List imageBytes) async {
  final cacheDir = await getCacheDirPath();
  final file = File('$cacheDir/$cacheKey.png');
  
  await file.writeAsBytes(imageBytes);
}

6. 完整业务逻辑示例

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class ImageCacheDemo extends StatefulWidget {
  final String imageUrl;

  const ImageCacheDemo({super.key, required this.imageUrl});

  @override
  State<ImageCacheDemo> createState() => _ImageCacheDemoState();
}

class _ImageCacheDemoState extends State<ImageCacheDemo> {
  Uint8List? _imageBytes;
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _loadImage();
  }

  Future<void> _loadImage() async {
    setState(() => _isLoading = true);
    
    final cacheKey = generateCacheKey(widget.imageUrl);
    // 优先读取缓存
    final cachedBytes = await getCachedImage(cacheKey);
    
    if (cachedBytes != null) {
      setState(() {
        _imageBytes = cachedBytes;
        _isLoading = false;
      });
      return;
    }

    // 缓存不存在,发起API请求
    try {
      final response = await http.get(Uri.parse(widget.imageUrl));
      if (response.statusCode == 200) {
        final bytes = response.bodyBytes;
        setState(() => _imageBytes = bytes);
        // 写入缓存
        await saveImageToCache(cacheKey, bytes);
      }
    } catch (e) {
      print('图片加载失败: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }
    return _imageBytes != null 
        ? Image.memory(_imageBytes!) 
        : const Center(child: Text('图片加载失败'));
  }
}

7. 权限配置(重要)

  • Android:在android/app/src/main/AndroidManifest.xml中添加:
    <!-- 临时缓存目录无需WRITE权限,用文档目录则需添加对应权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    
  • iOS:在ios/Runner/Info.plist中添加:
    <key>NSPhotoLibraryUsageDescription</key>
    <string>需要访问相册存储图片缓存</string>
    

扩展优化

  • 缓存清理:定期清理超过指定天数的缓存文件,避免占用过多存储空间
  • 缓存大小限制:设置总缓存大小阈值,超过时删除最旧的缓存文件
  • 错误重试:请求失败时添加重试逻辑,提升容错性
  • 二级缓存:结合内存缓存(如MemoryImage)实现内存+磁盘的二级缓存,加快加载速度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:45:36