all_image_cache 1.0.0 copy "all_image_cache: ^1.0.0" to clipboard
all_image_cache: ^1.0.0 copied to clipboard

Flutter image cache with memory/disk layers, network/file/bytes sources, SVG support, optional compression, stale fallback, and production-ready widgets.

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

4
likes
160
points
507
downloads

Documentation

API reference

Publisher

verified publisheropensource.tatamemaster.com.br

Weekly Downloads

Flutter image cache with memory/disk layers, network/file/bytes sources, SVG support, optional compression, stale fallback, and production-ready widgets.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#cache #image #svg #compression #flutter

License

MIT (license)

Dependencies

all_box, all_image_compress, flutter, flutter_svg, http, meta, path_provider

More

Packages that depend on all_image_cache