all_image_cache

Portugues | English

pub package license: MIT pub points 168 tests

Production-ready image caching for Flutter: memory + disk cache, network/file/bytes sources, SVG support, optional compression, stale fallback, negative caching, and widgets that are easy to test.

all_image_cache hero

Table of contents

Features

  • L1 + L2 cache - fast in-memory reads backed by persistent storage.
  • Network, file, and in-memory sources - NetworkImageSource, FileImageSource, and BytesImageSource.
  • Raster and SVG widgets - use CachedImage for raster images and CachedSvgImage for SVG.
  • Header-aware cache keys - authenticated image requests do not accidentally share cached bytes.
  • Optional compression - plug all_image_compress or your own ImageCompressor.
  • Stale fallback - expired bytes can still be returned when refresh fails.
  • Positive TTL - opt-in expiration per image, by default, or through a source-based resolver.
  • Negative caching - opt-in cache for known HTTP errors such as 404.
  • Web-aware defaults - web uses an in-memory L2 store by default.
  • Authoritative writes - putSourceBytes wins over older fetches and invalidates stale Flutter frames and negative-cache entries.
  • Decode recovery - invalid raster or SVG bytes are evicted after a real decode failure so a later rebuild can fetch valid content.
  • Protected byte ownership - cache hits expose immutable views instead of mutable internal buffers.
  • Hardened persistence - logical keys become SHA-256 filenames and inconsistent byte/metadata pairs self-clean.
  • Testable by design - the engine depends on contracts, so tests can inject fakes without real network or disk.

Requirements

The package supports Dart 3.3.0+ and Flutter 3.19.0+. This is the lowest real installable stable pair supported by all_box 1.0.0; the remaining direct dependencies deliberately allow their oldest compatible APIs.

The example development tooling has a separate Dart 3.10.0 / Flutter 3.38.0 floor because all_observer_lint 0.6.1 requires Dart 3.10. It does not raise the SDK requirement for applications consuming all_image_cache.

CI validates the package on Flutter 3.19.0 with the lowest allowed dependency versions and on the current stable Flutter release. See minimum-version policy.

Installing

flutter pub add all_image_cache
dependencies:
  all_image_cache: ^1.0.0
import 'package:all_image_cache/all_image_cache.dart';

Quick start

After installing the package and importing all_image_cache, most apps only need two things:

  1. Initialize the cache once in main.
  2. Render the image with CachedImage or CachedSvgImage.

Initialize the cache before runApp.

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await AllImageCache.init();

  runApp(const MyApp());
}

Render a cached network image.

class UserAvatar extends StatelessWidget {
  const UserAvatar({super.key});

  @override
  Widget build(BuildContext context) {
    return CachedImage.network(
      'https://example.com/avatar.jpg',
      width: 96,
      height: 96,
      fit: BoxFit.cover,
      placeholderBuilder: (_) => const CircularProgressIndicator(),
      errorBuilder: (_, error) => const Icon(Icons.broken_image),
    );
  }
}

That is enough for the default setup. The widget will fetch the image, store it in memory + disk cache, reuse it on the next render, and show your placeholder/error widgets when needed.

Step-by-step usage

1. Add the package

flutter pub add all_image_cache

2. Import it

import 'package:all_image_cache/all_image_cache.dart';

3. Initialize once

Call AllImageCache.init() before any cached image is rendered. A good place is main(), after WidgetsFlutterBinding.ensureInitialized().

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await AllImageCache.init();
  runApp(const MyApp());
}

4. Use a cached raster image

Use CachedImage for normal images such as JPG, PNG, WebP, GIF, and BMP.

CachedImage.network(
  'https://example.com/products/42.jpg',
  width: 120,
  height: 120,
  fit: BoxFit.cover,
  placeholderBuilder: (_) => const Center(
    child: CircularProgressIndicator(),
  ),
  errorBuilder: (_, error) => const Icon(Icons.broken_image),
)

5. Use a cached SVG

SVG is supported, but it uses a separate widget because Flutter's normal image decoder does not decode SVG.

CachedSvgImage.network(
  'https://example.com/icons/cart.svg',
  width: 32,
  height: 32,
)

6. Use a local file

CachedImage.file(
  imageFile.path,
  width: 160,
  height: 160,
  fit: BoxFit.cover,
)

For local SVG files:

CachedSvgImage.file(
  svgFile.path,
  width: 48,
  height: 48,
)

7. Use bytes already in memory

When you already have image bytes, provide a stable uniqueKey. The key is how the cache identifies those bytes later.

CachedImage.memory(
  bytes,
  uniqueKey: 'user-avatar-42',
  width: 96,
  height: 96,
  fit: BoxFit.cover,
)

For SVG bytes:

CachedSvgImage.memory(
  svgBytes,
  uniqueKey: 'brand-logo-svg',
  width: 120,
  height: 40,
)

8. Use authenticated image URLs

Headers are part of the cache key. This prevents two users or two tokens from accidentally sharing cached bytes.

CachedImage.network(
  'https://api.example.com/me/avatar',
  headers: {
    'Authorization': 'Bearer $token',
  },
  ttl: const Duration(hours: 6),
)

9. Use TTL for images that can change

By default, positive image entries do not expire by time. They are reused until the URL/cache key changes, the entry is evicted by cleanup, or the cache is cleared.

For images that may change while keeping the same URL, pass a TTL:

CachedImage.network(
  'https://api.example.com/me/avatar',
  ttl: const Duration(hours: 6),
)

For the direct manager API:

final bytes = await AllImageCache.instance.getBytes(
  NetworkImageSource('https://api.example.com/me/avatar'),
  ttl: const Duration(hours: 6),
);

When the backend can provide a version, prefer versioned URLs such as ?v=updatedAt or ?v=hash. TTL is useful as a fallback for URLs that cannot be versioned.

10. Use it where Flutter requires ImageProvider

Use CachedImageProvider for raster images in APIs like DecorationImage.

Container(
  decoration: const BoxDecoration(
    image: DecorationImage(
      image: CachedImageProvider(
        NetworkImageSource('https://example.com/background.jpg'),
        ttl: Duration(days: 7),
      ),
      fit: BoxFit.cover,
    ),
  ),
)

Supported image types

all_image_cache caches bytes from network, file, or memory sources. Rendering support depends on which widget you use.

Image type Use Notes
JPEG / JPG CachedImage Supported by Flutter's raster decoder.
PNG CachedImage Supported by Flutter's raster decoder.
WebP CachedImage Supported by Flutter on the usual mobile, desktop, and web targets.
GIF CachedImage Supported by Flutter's image pipeline.
BMP CachedImage Supported where Flutter's decoder supports it.
Other Flutter raster formats CachedImage If Image.memory can decode it on the target platform, CachedImage can render it.
SVG CachedSvgImage Rendered through flutter_svg; do not use CachedImage for SVG.

Important distinction:

  • CachedImage is for raster images decoded by Flutter.
  • CachedSvgImage is for SVG.
  • The cache layer stores bytes; the widget decides how those bytes are rendered.
  • Compression is intended for raster images. SVG entries are kept away from the raster compression path.

Widgets

CachedImage

Use it for raster formats decoded by Flutter, such as PNG, JPEG, WebP, GIF, BMP, and platform-supported image formats.

CachedImage.memory(
  bytes,
  uniqueKey: 'app://avatar/42',
  cacheWidth: 192,
  cacheHeight: 192,
)

CachedSvgImage

Use it for SVG. SVG bytes are cached by the same engine but rendered through flutter_svg.

CachedSvgImage.network(
  'https://example.com/icon.svg',
  width: 32,
  height: 32,
)

CachedImageProvider

Use it when Flutter asks for an ImageProvider, for example DecorationImage.

Container(
  decoration: const BoxDecoration(
    image: DecorationImage(
      image: CachedImageProvider(
        NetworkImageSource('https://example.com/background.jpg'),
      ),
      fit: BoxFit.cover,
    ),
  ),
)

CachedImageProvider is for raster images. Use CachedSvgImage for SVG.

CachedImageProvider is only an ImageProvider: it participates in Flutter's global ImageCache, but it cannot own placeholder, fade, crossfade, or other UI. For an animated Container, ClipRRect, CircleAvatar, or Stack, use CachedImage.imageBuilder:

CachedImage.network(
  'https://example.com/background.jpg',
  fadeInDuration: const Duration(milliseconds: 300),
  fadeOutDuration: const Duration(milliseconds: 200),
  imageBuilder: (context, provider) {
    return Container(
      decoration: BoxDecoration(
        image: DecorationImage(
          image: provider,
          fit: BoxFit.cover,
        ),
      ),
    );
  },
)

The placeholder remains visible during fetch and decode. The custom builder is revealed only after the provider publishes its first frame. The default raster path and imageBuilder both support fadeInDuration, fadeOutDuration, fadeInCurve, and fadeOutCurve; omitted fadeOutDuration falls back to fadeInDuration.

The default CachedSvgImage path waits until flutter_svg reports successfully parsed, paint-ready content. A custom svgBuilder receives raw cached bytes and owns its rendering pipeline, so the package cannot detect that custom renderer's first paint; its transition begins when the custom builder is mounted.

Cache-key migration

The default generator uses a local SHA-256 implementation over a canonical source representation (source type, URL/path/unique key, and sorted headers). Keys carry a v2_ prefix. Legacy UUID keys are intentionally not read because they may contain collided data; they are left untouched and naturally age out instead of being cleared on every initialization.

Starting with 0.0.8, the default file store maps each logical key to a SHA-256 .blob filename. Public cache keys do not change. Existing files that used raw logical names become cold misses and are fetched again; normal cache cleanup or clear() removes the old files.

Direct manager API

You can read, replace, or warm bytes directly when building your own UI.

final bytes = await AllImageCache.instance.getBytes(
  NetworkImageSource('https://example.com/photo.webp'),
);

await AllImageCache.instance.putSourceBytes(
  NetworkImageSource('https://example.com/me/avatar'),
  uploadedAvatarBytes,
);

await AllImageCache.instance.precache(
  NetworkImageSource('https://example.com/icon.svg'),
);

final l2Stats = await AllImageCache.instance.l2Stats();

await AllImageCache.instance.evictSource(
  NetworkImageSource('https://example.com/photo.webp'),
);

final freshBytes = await AllImageCache.instance.refresh(
  NetworkImageSource('https://example.com/photo.webp'),
);

l2Stats() reports only the L2 layer; it excludes L1 memory and Flutter's decoded ImageCache.

putSourceBytes is authoritative: it invalidates older in-flight work, negative-cache state, and decoded provider identity before storing the new bytes. The lower-level putBytes remains available when you already own the generated cache key and is supported as an advanced API throughout 1.x.

Invalidation uses source identity; callers do not need to generate or persist internal cache keys. See cache consistency and concurrency for the L1/L2/Flutter ImageCache policy.

Configuration

await AllImageCache.init(
  maxEntries: 300,
  maxSizeBytes: 200 * 1024 * 1024,
  memoryCacheMaxEntries: 120,
  memoryCacheMaxSizeBytes: 64 * 1024 * 1024,
  networkTimeout: const Duration(seconds: 30),
  maxDownloadBytes: 20 * 1024 * 1024,
  defaultTtl: const Duration(days: 7),
  ttlResolver: (source) {
    if (source is NetworkImageSource &&
        source.url.contains('/avatar/')) {
      return const Duration(hours: 6);
    }
    return null;
  },
  enableCompression: true,
  negativeCacheDuration: const Duration(minutes: 5),
  negativeCacheStatusCodes: const {404},
  logger: const NoopCacheLogger(),
);

Everything important is injectable:

final manager = ImageCacheManager(
  l1: myMemoryStore,
  l2: myPersistentStore,
  fetchers: [myFetcher],
  keyGenerator: ValidationKeyGenerator(),
  compressor: const NoopImageCompressor(),
);

Compression before saving

Yes, all_image_cache can reduce raster images before saving them in the persistent cache.

Enable compression during initialization:

await AllImageCache.init(
  enableCompression: true,
);

Tune quality and maximum dimensions:

await AllImageCache.init(
  enableCompression: true,
  compressOptions: const ImageCompressOptions(
    quality: 72,
    maxWidth: 1080,
    maxHeight: 1080,
  ),
);

What happens:

  1. The original bytes are fetched from network, file, or memory.
  2. Raster bytes are compressed.
  3. The compressed bytes are saved in L2 disk cache.
  4. Future reads use the smaller cached bytes.

Use this when remote images are much larger than your UI needs, such as product grids, avatars, feeds, galleries, or chat images.

Example result:

Before saving in cache

Original raster bytes       ████████████████████ 100%
Compressed raster bytes     ████████              ~40%
SVG bytes                   ████████████████████ 100% (not compressed)

Compression support by format:

Format Compressed before saving? Why
JPEG / JPG Yes Raster format; useful for reducing quality and dimensions.
PNG Yes Raster format; useful when dimensions are larger than the UI needs.
WebP Yes Raster format; can be reduced before persistent cache writes.
GIF / animated images Depends on compressor support Keep tests for your target format if animation matters.
BMP and other raster formats Depends on compressor support Cache works; compression depends on the configured compressor.
SVG No SVG is XML/vector data and is rendered by flutter_svg, not the raster decoder.

You can also provide your own compressor:

await AllImageCache.init(
  compressor: MyImageCompressor(),
  compressOptions: const ImageCompressOptions(
    quality: 80,
    maxWidth: 1600,
  ),
);

compressor overrides enableCompression. SVG files are not sent through the raster compression path.

SVG support

CachedImage intentionally stays raster-only. SVG is a different rendering path, so all_image_cache exposes CachedSvgImage.

This prevents the classic Could not instantiate image codec error that happens when SVG bytes are sent to Flutter's raster image decoder.

SVG bytes are also skipped by the compression step, so enabling compression for JPEG/PNG does not corrupt SVG entries.

Memory and decode size

Large images in long lists can exhaust memory if decoded at full resolution. Use cacheWidth and cacheHeight on CachedImage to request a smaller decoded image.

CachedImage.network(
  url,
  width: 80,
  height: 80,
  cacheWidth: 160,
  cacheHeight: 160,
)

This wraps the image provider with ResizeImage.resizeIfNeeded.

Successful cache insertion makes one defensive copy. Cache hits return unmodifiable views of that owned buffer, so callers cannot mutate cached content and repeated reads do not copy the entire image again.

Negative cache and stale fallback

By default, HTTP errors are not cached. If you want to avoid repeatedly requesting known-missing images, enable negative cache:

await AllImageCache.init(
  negativeCacheDuration: const Duration(minutes: 10),
  negativeCacheStatusCodes: const {404},
);

Expired entries are still refreshed when possible. The default stale policy is conservative: it returns stale bytes only for transport failures, configured network timeouts, and HTTP 5xx responses. It does not mask 401/403/404, compression failures, storage failures, download-limit errors, or programming errors.

You can replace that decision explicitly:

await AllImageCache.init(
  staleFallbackPolicy: (source, error) {
    return error is ImageHttpException && error.statusCode == 404;
  },
);

When Flutter or flutter_svg reports an actual decode failure, the failed bytes are evicted only if they are still the current L1/L2 content. A stale decode failure cannot remove bytes written later by putSourceBytes or refresh. The package does not clear Flutter's global image cache and does not retry automatically, preventing retry loops; a later rebuild may fetch corrected content.

Persistence and lifecycle

Outside Web, each all_box container has an independent versioned blob namespace. Blob updates are staged in a unique temporary file, flushed, and renamed into place while the previous version remains available for rollback. A flushed transaction marker is cleared only after the metadata commit; a marker left by an interrupted process invalidates the ambiguous blob generation. Malformed metadata and interrupted cache writes therefore become cold misses instead of incorrect hits, including same-size overwrites.

Required metadata commits await the all_box 1.0.0 writeAndSave durability tier. Optimistic LRU touch updates remain debounced; asynchronous persistence failures are emitted as CacheEventType.persistenceError.

Reinitialization disposes the previous manager before opening the replacement. You can also release owned HTTP and metadata resources explicitly without deleting cached entries:

await AllImageCache.dispose();

See cache semantics for the complete concurrency, stale, persistence, and lifecycle contract.

Testing

The engine is built around public contracts, so tests can avoid real HTTP and disk:

final manager = ImageCacheManager(
  l1: FakeStore(),
  l2: FakeStore(),
  fetchers: [FakeFetcher()],
  keyGenerator: ValidationKeyGenerator(),
);

The package has 168 tests covering memory/disk hits, authoritative writes, concurrency, stale fallback, negative cache, raster/SVG decode recovery, headers, cleanup, compression, storage consistency, and the example app.

flutter test
cd example
flutter test
flutter build web

Comparison

Package Raster cache SVG Custom stores/fetchers Stale fallback Negative cache
all_image_cache Yes Yes, via CachedSvgImage Yes Yes Opt-in
cached_network_image Yes No built-in SVG widget Cache-manager based Partial/stale patterns vary No built-in negative cache
cached_network_svg_image SVG-focused Yes Narrower API Limited No built-in negative cache

When to use it

Use all_image_cache when your app needs a single image cache layer for network images, local files, memory bytes, SVG assets, authenticated URLs, custom storage/fetching, and testable behavior.

Use a simpler widget if you only need Image.network with no persistence, no custom cache behavior, and no test doubles.

Documentation

Contributors

Contributors

Made with contrib.rocks.

How to contribute

Contributions are welcome. Read CONTRIBUTING.md to get started.


License

MIT

Libraries

all_image_cache
all_image_cache - Flutter image cache for network, file, and in-memory images, with L1 memory cache, L2 disk cache, SVG support, optional compression, stale fallback, negative caching, and ready-to-use widgets.