all_image_cache 1.0.0
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
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.

Table of contents #
- Features
- Requirements
- Installing
- Quick start
- Step-by-step usage
- Supported image types
- Widgets
- Direct manager API
- Configuration
- Compression before saving
- SVG support
- Memory and decode size
- Negative cache and stale fallback
- Persistence and lifecycle
- Testing
- Comparison
- When to use it
- Documentation
- Contributors
- How to contribute
Features #
- L1 + L2 cache - fast in-memory reads backed by persistent storage.
- Network, file, and in-memory sources -
NetworkImageSource,FileImageSource, andBytesImageSource. - Raster and SVG widgets - use
CachedImagefor raster images andCachedSvgImagefor SVG. - Header-aware cache keys - authenticated image requests do not accidentally share cached bytes.
- Optional compression - plug
all_image_compressor your ownImageCompressor. - 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 -
putSourceByteswins 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:
- Initialize the cache once in
main. - Render the image with
CachedImageorCachedSvgImage.
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:
CachedImageis for raster images decoded by Flutter.CachedSvgImageis 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:
- The original bytes are fetched from network, file, or memory.
- Raster bytes are compressed.
- The compressed bytes are saved in L2 disk cache.
- 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 #
- Example app
- Public API stability for 1.x
- Minimum supported versions
- Cache semantics
- Cache consistency and concurrency
- API reference
Contributors #
Made with contrib.rocks.
How to contribute #
Contributions are welcome. Read CONTRIBUTING.md to get started.
License #
MIT