p-hatan/ mirror

pub repository

mirror.p-hatan.com

cached_network_image_ce

4.13.1

Community Edition Flutter library to load and cache network images. Can also be used with placeholder and error widgets.

Cached in Iran · upstream_checksum

Download archive

Readme

Cached Network Image (Community Edition)

A Flutter library to show images from the internet and keep them in the cache directory.

This is the actively maintained, high-performance community fork of cached_network_image, powered by hive_ce instead of sqflite for up to 8x faster cache reads and smoother scrolling.

For the full pitch, architecture diagram, benchmarks, and package comparison table, see the repo README. This page is the complete API reference.


Why this fork exists

The original cached_network_image package by Baseflow is a titan in the Flutter ecosystem, used by millions. However, it has been effectively unmaintained since August 2024, leaving over 300 issues unresolved, including critical memory leaks and scroll performance bugs.

As the Flutter ecosystem evolved, the original architecture began to show its age. It relied on sqflite for cache management: a heavy, SQL-based solution that requires platform channels to communicate with native code. For a simple task like "checking if an image exists," this overhead caused UI jank in heavy lists.

We created the Community Edition (_ce) to fix this. We didn't just fork it to merge dependabot PRs. We re-engineered the caching layer.

The architectural shift: SQLite vs. Hive

We replaced the heavy sqflite dependency with hive_ce.

  • Old way (sqflite): serialized data → Platform Channel → Java/Obj-C → SQLite → Disk. Slow, blocking.
  • New way (hive_ce): Dart Memory → Direct Disk Access. Instant, non-blocking.

The result: zero-jank scrolling.

Benchmarks

We benchmarked the cache metadata operations (checking, writing, and deleting cache entries) on an iPhone Simulator. The results speak for themselves:

Operation Payload Size Original (sqflite) CE (hive_ce) Improvement
Read (Hit Check) 10 KB 16 ms 2 ms 8.00x faster
Write (New Image) 10 KB 116 ms 29 ms 4.00x faster
Delete (Cleanup) 10 KB 55 ms 19 ms 2.89x faster
Read (Large) 1 MB 8 ms 1 ms 8.00x faster

Note: "Read" is the most critical operation for scrolling performance, as every list item checks the cache before rendering.


Features

It's a drop-in replacement, about 99% API compatible with the original package. Cache lookups go through hive_ce, so they're fast and non-blocking. Unlike the original package, it caches images fully and persistently on the web via IndexedDB, and avoids RAM spikes by decoding at native image sizes instead of full resolution. HTTP and cache interceptor chains let you add auth headers, inject synthetic responses, or skip caching selectively, all without forking the package. It's still getting regular updates and bug fixes too.

Installation

Install with:

flutter pub add cached_network_image_ce

How to use

The API is identical to the original package. You can use CachedNetworkImage directly or via ImageProvider. Both approaches are fully supported with persistent IndexedDB caching on the web platform out of the box.

Basic Usage with Placeholder

import 'package:cached_network_image_ce/cached_network_image.dart';

CachedNetworkImage(
  imageUrl: 'https://via.placeholder.com/350x150',
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
),

With Progress Indicator

CachedNetworkImage(
  imageUrl: 'https://via.placeholder.com/350x150',
  progressIndicatorBuilder: (context, url, downloadProgress) =>
      CircularProgressIndicator(value: downloadProgress.progress),
  errorWidget: (context, url, error) => Icon(Icons.error),
),

Advanced Usage (ImageBuilder)

Use this when you need an ImageProvider for things like DecorationImage:

CachedNetworkImage(
  imageUrl: 'https://via.placeholder.com/200x150',
  imageBuilder: (context, imageProvider) => Container(
    decoration: BoxDecoration(
      image: DecorationImage(
        image: imageProvider,
        fit: BoxFit.cover,
        colorFilter: ColorFilter.mode(Colors.red, BlendMode.colorBurn),
      ),
    ),
  ),
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
),

Direct ImageProvider Usage

Image(image: CachedNetworkImageProvider(url))

Network Timeouts (DefaultCacheManager)

You can configure network timeouts at the cache manager level using ConnectionParameters.

final cacheManager = DefaultCacheManager(
  connectionParameters: ConnectionParameters(
    connectionTimeout: const Duration(seconds: 10),
    requestTimeout: const Duration(seconds: 30),
  ),
);

CachedNetworkImage(
  imageUrl: 'https://example.com/image.jpg',
  cacheManager: cacheManager,
  errorBuilder: (context, url, error) => const Icon(Icons.error),
)

Timeout behavior:

  • connectionTimeout: max time waiting for response headers.
  • requestTimeout: inactivity timeout while streaming response bytes.

Both fields are optional and nullable. If connectionParameters is not provided, existing behavior is preserved (no timeout is applied).

On Flutter Web, timeout settings apply when using ImageRenderMethodForWeb.HttpGet. The HtmlImage render method uses the browser image pipeline and bypasses the cache manager HTTP path.

Cache and Metadata Directories

On native IO targets, cached image files and Hive metadata can use separate base directories.

final cacheManager = DefaultCacheManager(
  cacheDirectoryProvider: getTemporaryDirectory,
  metadataDirectoryProvider: getApplicationSupportDirectory,
);

If metadataDirectoryProvider is omitted, metadata stays under the cache directory:

<cacheBase>/cached_network_image_ce/
<cacheBase>/cached_network_image_ce/hive/

Web Platform Headers Support: Custom headers (like Authorization) are only supported with ImageRenderMethodForWeb.HttpGet. The default HtmlImage render method does not support custom headers. If you need to send headers to authenticate image requests on Web, you must explicitly set:

CachedNetworkImage(
  imageUrl: 'https://api.example.com/image.jpg',
  httpHeaders: {'Authorization': 'Bearer token'},
  imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
),

Note: Using HttpGet on Web disables the browser's default image caching mechanisms, so images are cached via the cache_manager instead.

HTTP Interceptors

Intercept and modify HTTP requests, responses, and errors before they reach the cache layer. Useful for adding auth headers, logging, retrying, or short-circuiting with a synthetic response.

import 'package:cached_network_image_ce/cached_network_image.dart';

class AuthInterceptor extends HttpInterceptor {
  final String token;
  const AuthInterceptor(this.token);

  @override
  void onRequest(HttpRequestData request, HttpRequestHandler handler) {
    request.headers['Authorization'] = 'Bearer $token';
    handler.next(request); // pass (possibly mutated) request to next interceptor
  }
}

final manager = DefaultCacheManager(
  httpInterceptors: [AuthInterceptor('my-token')],
);

Each hook must call exactly one of handler.next, handler.resolve, or handler.reject. Failing to call any will stall the pipeline permanently.

Hook Trigger Handler methods
onRequest Before HTTP call next(request) · resolve(response) · reject(error)
onResponse After HTTP response received next(response) · resolve(response) · reject(error)
onError HTTP call threw next(error, st) · resolve(response) · reject(error)

Cache Interceptors

Intercept cache hit, miss, and store events to control how entries are read from and written to the cache index. Only available on native IO targets (Android, iOS, macOS, Linux, Windows); not available on web.

import 'package:cached_network_image_ce/cached_network_image.dart';

// Reject storage for specific URLs (e.g. private/sensitive images).
class PrivateImageInterceptor extends CacheInterceptor {
  @override
  void onStore(CacheStoreData data, CacheStoreHandler handler) {
    if (data.url.contains('/private/')) {
      handler.reject(); // skip writing to Hive; file is delivered then deleted
    } else {
      handler.next(data);
    }
  }
}

final manager = DefaultCacheManager(
  cacheInterceptors: [PrivateImageInterceptor()],
);
Hook Trigger Handler methods
onHit Cached entry found and valid next(data) · resolve(fileInfo) · reject (force re-download)
onMiss No cached entry found next(data) · resolve(fileInfo) (skip download entirely)
onStore After download, before writing metadata next(data) · reject (skip persisting; cache file deleted)

When onStore rejects, the downloaded file is copied to a system-temp location, the cache-directory copy is deleted (no orphan), and the temp copy is yielded to the caller for this request only.

Pre-caching Images

Download and cache images before they appear on screen, so navigation feels instant. preCache reuses the same cache manager and key logic as the widget.

// Warm the cache ahead of time (e.g. in initState or a splash screen).
await CachedNetworkImage.preCache(
  imageUrl: 'https://example.com/hero.jpg',
);

// Later, the widget loads from cache with no network wait.
CachedNetworkImage(imageUrl: 'https://example.com/hero.jpg')

All parameters are optional except imageUrl:

await CachedNetworkImage.preCache(
  imageUrl: 'https://example.com/hero.jpg',
  cacheKey: 'hero',
  headers: {'Authorization': 'Bearer token'},
  cacheManager: myCustomCacheManager,
  maxWidthDiskCache: 800,
  maxHeightDiskCache: 600,
  timeout: const Duration(seconds: 10),
);

Returns a FileInfo with the cached file path, expiry, and source. Waits for an expired cache entry to finish refreshing before returning, and throws a StateError instead of returning a stale file if that refresh fails. timeout, if given, throws a TimeoutException when exceeded but does not cancel the underlying download. maxWidthDiskCache / maxHeightDiskCache throw an ArgumentError when cacheManager isn't an ImageCacheManager.

Animated Images (GIF)

Set animate: false to show only the first frame of a multi-frame image. The remaining frames are never decoded.

CachedNetworkImage(
  imageUrl: 'https://example.com/animation.gif',
  animate: false,
)

Only the frame callbacks and the getNextFrame() calls for the remaining frames are skipped. The whole file is still downloaded, cached, read back and decoded into a codec, so animate: false is not a way to save bandwidth or memory on large GIFs.

animate is part of the image provider key, so toggling it at runtime resolves a different image stream. The widget paints nothing while that stream resolves, and which frame it starts on depends on whether its key is still held by the ImageCache — an animation that played before is likely to resume where it left off rather than restart. To pause and resume in place without changing the key, wrap the widget in a TickerMode instead:

TickerMode(
  enabled: !paused,
  child: CachedNetworkImage(imageUrl: 'https://example.com/animation.gif'),
)

MediaQueryData.disableAnimations pauses animated images the same way. Both need Flutter 3.41 or newer to show the first frame when the image loads while already paused (flutter/flutter#176492); on older versions a widget that mounts paused stays blank until it resumes. animate: false has no such requirement and works on every supported Flutter version.

On the web this only applies to ImageRenderMethodForWeb.HttpGet. The default, HtmlImage, decodes through an <img> element whose codec reports a single frame (HtmlImageElementCodec.frameCount => 1 in the Flutter web engine), so animated images never play there and there is nothing for animate or TickerMode to stop:

CachedNetworkImage(
  imageUrl: 'https://example.com/animation.gif',
  imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
)

If you need full playback control (play, pause, seek), read the bytes from the cache manager yourself (DefaultCacheManager().getFileStream(url), or DefaultCacheManagerWeb on the web) and hand them to a package such as gif_view. CachedNetworkImageProvider cannot do this: every load path it offers returns an ImageStreamCompleter, never the bytes.

Unsupported Image Formats (SVG, JXL, AVIF, HEIC, ...)

Flutter's built-in image codec can't decode every format. SVG never works (it's not a raster format), and some raster formats (JXL, AVIF, HEIC, ...) may fail depending on platform/OS codec support. When CachedNetworkImage detects SVG bytes, or the codec otherwise fails to decode the cached bytes, it throws an UnsupportedImageFormatException instead of leaving you with an opaque decode error. Use the unsupportedImageBuilder callback to render the raw bytes with a format-specific package of your choice, for example flutter_svg for SVG, or flutter_avif for AVIF:

Before using SvgPicture.memory, add flutter_svg to pubspec.yaml (dependencies: flutter_svg: ^2.2.1) and import package:flutter_svg/flutter_svg.dart.

CachedNetworkImage(
  imageUrl: 'https://example.com/image.svg',
  unsupportedImageBuilder: (context, url, bytes) {
    // `bytes` are the already-cached file bytes.
    return SvgPicture.memory(bytes); // from flutter_svg
  },
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
),

The image is still downloaded and cached normally; only the rendering path is different. If unsupportedImageBuilder is not set, the error falls through to errorWidget with an UnsupportedImageFormatException.

SVG is detected ahead of time, so its exception carries detectedFormat: 'svg'. Any other codec decode failure (JXL, AVIF, HEIC, or a genuinely corrupt file) is only caught once decoding is actually attempted, so detectedFormat is null in that case. Check the bytes yourself (e.g. magic numbers) if you need to distinguish which format/decoder to use inside unsupportedImageBuilder.

Web caveat: this whole mechanism requires the raw cached bytes, so it only works with ImageRenderMethodForWeb.HttpGet. The default HtmlImage render method hands the URL straight to the browser's native image pipeline and never has bytes to inspect, so a decode failure there falls straight through to errorBuilder/errorWidget as a plain error, not an UnsupportedImageFormatException.

FAQ

Q: Will I lose my users' existing cache if I migrate? A: Yes. Because we switched the storage engine from SQLite to Hive, the old cache files will be ignored. Users will re-download images once as they browse. This is a one-time migration cost for a permanent performance gain.

Q: My app crashes/pauses on errors? A: In Debug mode, Flutter may pause on exceptions even if they are caught. This is expected behavior for network errors (404s). In Release mode, these are handled silently by the errorWidget.

Q: Why is web caching slower or using Hive for image bytes? A: On Mobile & Desktop (IO), this package stores image bytes directly on the native file system and uses Hive only for metadata. The Web platform, however, lacks a native file system. So for web we use hive_ce to store both metadata and the actual image bytes in IndexedDB. Serializing large byte arrays in and out of IndexedDB introduces overhead that isn't present on IO. Alternative: If persistent caching across sessions isn't critical for your web users, consider conditionally using the standard Image.network on the web, which relies on the browser's built-in memory/HTTP caching to achieve faster decoding.

Contributing

We welcome contributions! If you want to help maintain this essential package, please check the CONTRIBUTING.md.

License

This project is licensed under the MIT License.

Changelog

[4.13.1] - 2026-09-26

  • Fix: Animated images played slower than their frame durations, e.g. 20ms GIF frames at 60% speed on a 60Hz display, because MultiImageStreamCompleter rounded every frame up to the next vsync. Frames now follow their durations on the wall-clock timeline, skipping frames that are already late when decoding is fast enough to catch up (flutter/flutter#24804).

[4.13.0] - 2026-09-26

  • Feature: maxWidthDiskCache / maxHeightDiskCache now resize WebP images on disk. Before, WebP files were stored at full size. As with JPEG and PNG, this applies when the URL path ends in .webp. Requested in issue #78.
  • Fix: Animated images (animated WebP, APNG) are no longer resized on disk. Resizing keeps only the first frame, so they are cached as the original file and keep animating.

[4.12.0] - 2026-09-08

  • Feature: Added animate to CachedNetworkImage and CachedNetworkImageProvider (default true). With animate: false a multi-frame image such as a GIF shows its first frame and stops there, without decoding the rest. animate is part of the provider key, so toggling it at runtime resolves a different image stream: the widget paints nothing while that stream resolves, and which frame it starts on depends on what the ImageCache still holds. Wrap the widget in a TickerMode to pause and resume an animation in place. Requested in issue #21.
  • Fix: CachedNetworkImage.evictFromCache now forwards cacheKey to the provider it evicts and clears both the animate: true and animate: false entries, so the in-memory ImageCache eviction is no longer a silent no-op for images rendered with a cacheKey or with animate: false.

[4.11.1] - 2026-09-08

  • Fix: Images turned black on web after the widget was scrolled out of view and back. MultiImageStreamCompleter re-decoded the codec on every 0→1 listener transition; for a single-frame image on CanvasKit that produces a second lazy ui.Image sharing one <img> element, and emitting it disposes the previous image, which clears that element's src. The already decoded frame is now handed to the returning listener instead. Animated images still resume decoding. Reported by @tranhuudang (issue #67).
  • Fix: The completer handed the codec's own ui.Image to ImageInfo instead of a clone and never disposed _nextFrame, so every decode that was abandoned (listener dropped mid-decode) or superseded leaked a ui.Image. Frames are now cloned into ImageInfo and the decoded frame is disposed once handed over, matching the framework's MultiFrameImageStreamCompleter. That is also the ownership bug behind the black image above: setImage disposing the previous ImageInfo no longer releases a handle the codec's decoded frame still owns.
  • Fix: A frame could be emitted under a codec that had already replaced the one it was decoded from. _decodeNextFrameAndSchedule now captures its codec and discards the frame if the codec changed during the await, instead of evaluating frameCount against the new codec — which could terminate a running animation and mis-count the new codec's frames.
  • Fix: Adding a listener while a decode was already in flight started a second, concurrent decode of the same codec, so two frames could be emitted and dispose each other's image. addListener no longer starts a decode while one is pending.
  • Fix: A null-check crash when a replacement codec arrived between an animated frame being decoded and its scheduled app-frame callback running, or when a listener removed and re-added itself from the setImage callback. Both released the pending frame while _handleAppFrame was still reading it. The callback now takes the frame before notifying listeners and returns quietly if the frame it was scheduled for is already gone, and decoding a codec that already has a frame in flight is a no-op.
  • Fix: Replaced codecs are now disposed instead of dropped, so a cache hit followed by a network refresh no longer retains the outgoing decoder for the life of the completer. A codec replaced while one of its frames is still decoding is disposed by that decode when it returns, since disposing it earlier would fail the pending getNextFrame().

[4.11.0] - 2026-09-01

  • Feature: Added CachedNetworkImage.preCache() static method for downloading and caching images without rendering them. Useful for warming the cache before navigation. Supports optional cacheKey, headers, custom cacheManager, disk-resize parameters (maxWidthDiskCache, maxHeightDiskCache), and an optional timeout. Throws a StateError instead of silently returning a stale file when refreshing an expired cache entry fails, and an ArgumentError (not just an assert) when resize parameters are used with a CacheManager that isn't an ImageCacheManager.

[4.10.1] - 2026-08-26

  • Fix: PathNotFoundException when the background cleanup sweep deleted a cached file after getFileFromCache had already handed out its FileInfo but before the image was read. The sweep starts unawaited during initialization, so on cold start every first-frame load raced it. A cached file that disappears mid-read now falls back to a fresh download instead of throwing. Applies to the resize path too, which reads the separately-evictable original entry.
  • Fix: getImageFile removed its in-flight resize entry only after its stream completed, so abandoning that stream left every later call for the same key waiting on a stream that had already finished, and no image was ever delivered.
  • Fix: putFile now awaits its Hive metadata write instead of returning while the write is still unsequenced.
  • Behaviour change: The cleanup sweep skips entries served within the last 30 seconds, so an image still being loaded is not evicted out from under its reader. Skipped entries lose their place in the eviction order but not the size budget, so maxNrOfCacheObjects is still honoured.

reported by @aycasecr — thanks! (PR #62)

[4.10.0] - 2026-07-30

  • Performance: DefaultCacheManager now reuses a single HTTP client across downloads instead of creating and closing one per request, so connections can be kept alive between images.
  • Fix: A connection timeout now aborts the in-flight request instead of leaving it running in the background.
  • Fix: dispose() closes the reused client. Downloads started after disposal create their own client, which is closed once its last response is released.
  • Behaviour change: dispose() called while a download is in flight now aborts that download; previously each download owned its client and could outlive dispose().
  • Behaviour change: An onResponse interceptor that replaces the response must not derive the replacement body from the original response's stream — the original body is now canceled as soon as it is replaced, to release its connection.

contributed by @Chlx42 — thanks! (PR #58)

[4.9.1] - 2026-07-23

  • Fix: Codec decode failures (e.g. JXL, or AVIF/HEIC on platforms without native support) now route to unsupportedImageBuilder instead of errorBuilder. Previously only pre-detected formats (SVG) were routed this way via UnsupportedImageFormatException; any other codec decode failure fell through as a generic, untyped error.

[4.9.0] - 2026-06-19

  • Feature: Added metadataDirectoryProvider to DefaultCacheManager so Hive metadata can be stored separately from cached image files on IO platforms.
  • Fix: Isolate the orphan-file cleanup sweep per metadata directory, so two DefaultCacheManager instances sharing a cacheDirectoryProvider but different metadataDirectoryProvider no longer delete each other's files. dispose() now awaits any in-flight sweep instead of racing it.

[4.8.0] - 2026-06-16

  • Feature: Added LRU cache cleanup support to DefaultCacheManager.
    • New CleanupStrategy abstraction controls eviction order when the cache exceeds maxNrOfCacheObjects.
    • TtlCleanupStrategy — evicts soonest-to-expire entries first. Matches prior behaviour and remains the default.
    • LruCleanupStrategy — evicts least-recently-used entries first, based on a new touchedAt timestamp updated on every cache hit.
    • CacheEntryMetadata gains a nullable touchedAt field; legacy entries without it fall back to validTill via effectiveTouchedAt.
    • Pass a strategy via the new cleanupStrategy parameter on DefaultCacheManager, or implement CleanupStrategy for a custom eviction policy.

[4.7.0] - 2026-06-10

  • Feature: Added HTTP and cache interceptor support to DefaultCacheManager.
    • HttpInterceptor — intercept, mutate, or short-circuit HTTP requests, responses, and errors before they reach the cache layer.
    • CacheInterceptor — intercept cache hit, miss, and store events. Reject a hit to force re-download; resolve a miss to inject a synthetic file; reject a store to skip writing metadata to Hive.
    • Pass interceptors via the new httpInterceptors and cacheInterceptors parameters on DefaultCacheManager.
    • CacheInterceptor and its handler types are only available on native IO targets; a no-op stub is exported for web.
    • Full test coverage for all interceptor chain paths.

[4.6.4] - 2026-03-24

  • Fix: Resolve HiveError crashes caused by URL string keys exceeding 255 characters. Long cache keys are now automatically hashed using SHA-256 to remain under Hive's 255-character length limit.

[4.6.3] - 2026-03-15

  • Fix: Handle exceptions when closing the Hive cache box and Hive instance during dispose(). (PR #17)
    • PathNotFoundException (e.g. when the cache directory was deleted before dispose) is now silently swallowed so callers can dispose without wrapping in try-catch.
  • Feat: Ensure the cache directory exists before any file read/write operation. (PR #17)
    • Prevents PathNotFoundException on first use when the OS or test harness removes the temp directory between initialisation and the first cache put/get.
  • Added regression tests for cache-directory resilience and for safe disposal after emptyCache(). (PR #17)

[4.6.2] - 2026-03-04

  • Fix: Handle Hive box corruption gracefully during cache initialization.
    • When openBox encounters a corrupted box with unknown typeIds (e.g., from removed adapters), catch HiveError, safely delete the box, and retry initialization.
    • Cleans up orphaned cache files after box recovery to prevent stale data.
    • Regression tests added for box corruption recovery on both IO and web platforms.
    • Issue #12: Fixes production crash "HiveError: Cannot read, unknown typeId" reported via Firebase Crashlytics.

[4.6.1] - 2026-03-04

  • Fix: Re-export ConnectionParameters from the main cached_network_image_ce barrel file so it is accessible without a separate cached_network_image_platform_interface_ce import.
  • Fix: Added missing connectionParameters parameter to the stub DefaultCacheManager for unsupported platforms.
  • Added example demonstrating DefaultCacheManager with ConnectionParameters.

[4.6.0] - 2026-03-03

  • Feature: Added optional HTTP timeout support via ConnectionParameters on DefaultCacheManager.
    • connectionTimeout limits time waiting for response headers.
    • requestTimeout applies an inactivity timeout while streaming response bytes.
    • Existing behavior is preserved when connectionParameters is not provided.
  • Added tests for timeout behavior and client cleanup on timeout.

[4.5.0] - 2026-03-02

  • Feature: Added web platform support. The package can now be used in Flutter web apps.
    • Web caching uses Hive CE backed by IndexedDB — no new dependencies needed.
    • Image bytes and metadata are stored in Hive boxes on web instead of the file system.
    • Disk-based image resizing is skipped on web (originals are cached as-is).
    • The DefaultCacheManager is now conditionally imported per platform (IO/web).

[4.4.0] - 2026-02-27

  • Feature: Skip placeholder and fade animations when images are loaded from disk cache. Cached images now appear instantly without unnecessary visual flicker.
  • Added disablePlaceholderOnCacheHit parameter (default true) to opt out of this behavior.
  • CachedNetworkImage is now a StatefulWidget to support async cache pre-checking.

[4.3.0] - 2026-02-26

  • Feature/Deprecation: Added errorBuilder to CachedNetworkImage and CachedImageWidget, which aligns with Flutter's standard ImageErrorWidgetBuilder. The old errorWidget property is now deprecated.
  • Fix: Resolved a memory leak where errorListener would prevent CachedNetworkImage from being garbage collected. Uses addEphemeralErrorListener on Flutter >= 3.16.

[4.2.0] - 2026-02-23

  • Feature: Added SVG support via new unsupportedImageBuilder callback
    • New UnsupportedImageFormatException thrown when image format is not supported by Flutter's standard codec
    • New ImageFormatDetector utility with content sniffing for SVG detection
    • Images are still cached normally; only rendering path differs
    • Example app updated with SVG demo using flutter_svg
    • 24 new tests added (18 format detection + 6 widget integration)
    • See README for usage example

[4.1.1] - 2026-02-20

  • Fix: Removed global Hive.init() usage from DefaultCacheManager to avoid conflicts with host app Hive initialization
  • Fix: Added initialization synchronization in DefaultCacheManager to prevent parallel cold-start race conditions
  • Fix: Switched cache metadata box opening to explicit openBox(path: ...) on a private Hive instance for reliable isolation
  • Feature: Added optional cacheDirectoryProvider parameter to DefaultCacheManager so apps can control cache storage location
  • Tests: Added regression tests for Hive init conflicts, concurrent initialization races, and cache directory resilience scenarios

[4.1.0] - 2026-02-19

  • Feature: Added httpClientFactory parameter to DefaultCacheManager for HTTP client dependency injection, enabling easy mocking in tests
  • Refactor: Replaced raw Map<String, dynamic> with typed CacheEntryMetadata class for all cache metadata operations
  • Tests: Comprehensive test coverage (131 tests, 86% coverage)
    • DefaultCacheManager: HTTP download success/errors, progress reporting, header forwarding, cache hit/miss/stale behavior, cleanup logic (31 tests)
    • Leak tests: StreamController closure, http.Client cleanup, file sink closure, ImageLoader chunkEvents, MultiImageStreamCompleter disposal, Hive box lifecycle (19 tests)
    • ImageLoader, CachedNetworkImageProvider, CacheEntryMetadata coverage
  • Fix: Resolved analyzer warnings across all packages

[4.0.1] - 2026-02-19

  • Added repository field for pub.dev source code display
  • Updated README with benchmarks and architectural details
  • Updated dependencies to _ce suffixed packages

[3.4.1] - 2024-08-13

  • Target js_interop for Wasm support

[3.4.0] - 2024-04-10

  • Change how errors are reported by ImageLoader. Emitting errors as streams instead of re-throwing.
  • Improved debug console messages
  • Update dependencies
  • added static field defaultCacheManager to CachedNetworkImageProvider
  • Expose scale of CachedNetworkImageProvider on CachedNetworkImage

[3.3.1] - 2023-12-31

  • Adding an errorListener prevents automatic reporting to global error handler.

[3.3.0] - 2023-09-25

  • Add error to ErrorListener
  • Update to Dart 3
  • Remove load, use loadImage instead loadBuffer

Minor code clean-ups:

  • Add topics in pubspec
  • Specify types
  • Update example

[3.2.3] - 2022-11-25

  • Correctly dispose image stream handler

[3.2.2] - 2022-08-31

  • loadBuffer is added and used instead of load

[3.2.1] - 2022-05-16

  • Update to Flutter 3

[3.2.0] - 2021-11-29

  • Add option to set the log level of the cache manager, for example:
CachedNetworkImage.logLevel = CacheManagerLogLevel.debug;
  • Update cache manager dependency.

[3.1.0+1] - 2021-11-04

  • Update Readme

[3.1.0] - 2021-07-16

  • Separate Web and IO implementations

[3.0.0] - 2021-03-27

  • Migrate to null safety
  • Fix "Cannot clone a disposed image" error
  • Update dependencies.

[3.0.0-nullsafety] - 2021-01-02

  • Migrate to null safety

[2.5.1] - 2021-03-09

  • Update dependencies

[2.5.0] - 2020-12-22

  • Add support for resizing images in disk cache.
CachedNetworkImage(
  maxHeightDiskCache: 200,
  imageUrl: 'https://via.placeholder.com/3000x2000',
);

[2.4.1] - 2020-12-01

  • Fix a bug that an image is disposed when it shouldn't.

[2.4.0] - 2020-11-30

  • Added support for a cache key which is different from the image url.

[2.3.3] - 2020-10-16

  • Support for flutter_cache_manager 2.x.x.

[2.3.2] - 2020-09-10

  • Fixed placeholders and error widgets, those are not always centered anymore.
  • Evict an image from ImageCache if image fails to load.
  • Added method to evict an image from memory and disk cache.

[2.3.1] - 2020-08-25

  • Fixed fading when the image has no placeholder or progress indicator.

[2.3.0] - 2020-08-25

  • Improved image loading by using OctoImage. OctoImage uses the native callbacks of the ImageProvider instead of loading the file when the ImageWidget is build the first time.
  • Set minimal Flutter version to 1.20.0; first compatible stable version.
  • Added choice for rendering of images on web. Defaults to standard HTML ImageElement, but gives the option to use a custom HTTP Get for headers and skia support.
  • Use a MultiImageStreamCompleter for when an image that is available in the cache is updated.

[2.3.0-rc] - 2020-06-20

  • Added choice for rendering of images on web. Defaults to standard HTML ImageElement, but gives the option to use a custom HTTP Get for headers and skia support.
  • Use a MultiImageStreamCompleter for when an image that is available in the cache is updated.
  • Increase minimal version of Flutter to 1.19.0-2.0.pre (currently requires Beta) due to an error listener bug.

[2.3.0-beta.1] - 2020-05-28

  • Improved web support: support for headers and skia.

[2.3.0-beta] - 2020-05-20

  • Rewrote image widget by using OctoImage.

[2.2.0+1] - 2020-05-05

  • Fix ImageProvider not using provided headers.

[2.2.0] - 2020-04-30

  • Upgrades on CachedNetworkImageProvider:
    • Support for download progress.
    • Basic web support (no caching).

[2.1.0+1] - 2020-04-10

  • Update minimal Dart sdk version

[2.1.0] - 2020-04-10

  • Update CacheManager
  • Added option for progress indicator

[2.0.0] - 2019-12-31

  • Public release of 2.0 version

[2.0.0-rc.1] - 2019-11-04

  • Revert scaling of image due to issues with BoxFit.

[2.0.0-rc] - 2019-10-17

[1.1.3] - 2019-11-04

  • Revert scaling of image due to issues with BoxFit.

[1.1.2+1] - 2019-10-17

  • Fix for widgets declared with infinite size.

[1.1.2] - 2019-10-16

  • Add filterQuality property.
  • Scale image to size when showing in widget.
  • Better error handling.
  • Fix for useOldImageOnUrlChange.
  • Update cache manager to 1.1.2.

[1.1.1] - 2019-07-23

  • Updated cache manager for error handling fix

[1.1.0] - 2019-07-13

  • Improved performance
  • Keep fetched files in sync with filemanager.
  • Better error handling.
  • Added extra example to show the imageBuilder

[1.0.0] - 2019-06-27

  • Updated dependencies

[0.8.0] - 2019-05-06

  • Fixed compile error on informationCollector by temporarily disabling it.

[0.7.0] - 2019-03-06

  • BREAKING CHANGE: Renamed ErrorWidgetBuilder to LoadingErrorWidgetBuilder
  • LoadingErrorWidgetBuilder returns an Object instead of an Exception
  • Fixed BoxFit to also work when size is not defined

[0.6.2] - 2019-02-27

  • Added option to blend image with color
  • Added option in CacheManager to clear the cache

[0.6.1] - 2019-02-25 BREAKING CHANGES

  • No longer assume infinite size.

[0.6.0] - 2019-02-18 BREAKING CHANGES

  • Breaking changes in API and behaviour
  • Very much improved though
  • Adapted for new cache manager library
  • Completely rewritten image view
  • Now using builders for placeholder and error widgets
  • Added optional builder to customize the image

[0.5.1] - 2018-11-19

  • Fixed error throwing

[0.5.0] - 2018-10-13

  • Updated cache manager for http 0.12.0

[0.4.2] - 2018-08-30

  • Updated cache manager dependency

[0.4.1] - 2018-04-27

  • Improved error handling when a file could not be loaded.

[0.4.0] - 2018-04-14

  • Added optional headers.
  • Changed to Dart 2.0
  • Fixed bug when updating widget with new url

[0.3.0] - 2018-02-09

  • Added CachedNetworkImage with placeholder and error widgets.

[0.2.1] - 2018-01-08

  • Moved from OneFrameImageStreamCompleter to MultiFrameImageStreamCompleter.
  • Updated CacheManager dependency for critical bug fix.

[0.2.0] - 2017-12-29

  • Breaking change Removed CachedNetworkImage. From now on only the ImageProvider is supported. For a placeholder use FadeInImage. See also "Fallback for Network Images".
  • Moved CacheManager to a separate library for a more generic purpose.

[0.1.0] - 2017-12-21

  • Breaking change. Upgraded to Gradle 4.1 and Android Studio Gradle plugin 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in order to use this version. Instructions can be found here.

[0.0.2] - 10 December 2017

Added an ImageProvider and improved documentation

[0.0.1] - 2 December 2017

Initial release, should be polished

Installing

export PUB_HOSTED_URL=https://pub.mirror.p-hatan.com
flutter pub add cached_network_image_ce
pubspec.yaml
name: cached_network_image_ce
description: Community Edition Flutter library to load and cache network images.
  Can also be used with placeholder and error widgets.
homepage: https://github.com/Erengun/flutter_cached_network_image_ce
repository: https://github.com/Erengun/flutter_cached_network_image_ce/tree/develop/cached_network_image
topics:
  - cache
  - image
  - network-image
version: 4.13.1

environment:
  sdk: ^3.0.0
  flutter: '>=3.10.0'

dependencies:
  cached_network_image_platform_interface_ce: ^5.2.0
  cached_network_image_web_ce: ^2.1.0
  clock: ^1.1.1
  crypto: ^3.0.0
  file: '>=7.0.0 <8.0.0'
  flutter:
    sdk: flutter
  hive_ce: ^2.0.0
  http: ^1.6.0
  meta: ^1.9.0
  octo_image: ^2.1.0
  path: ^1.8.0
  path_provider: ^2.0.0

dev_dependencies:
  flutter_lints: ^4.0.0
  flutter_test:
    sdk: flutter
  mocktail: ^1.0.4

Version history

VersionPublishedArchiveSHA-256
4.0.02026-02-19T10:06:45.468665ZCached5b102eeb7a0278f807e2a642d8efa7d206e13ea7792e24afdb7670e59e5d62ee
4.0.12026-02-19T10:19:04.924300ZCachedbede5b1e74e8c99be0ee867e9e35d5fc632b09b6cf18ab74a94f6e0b2e8351a8
4.1.02026-02-20T08:25:51.541871ZCached78e06dad3ea819da22699a369a49b82f5cbb49661e192ad24bd7b2c5a1c2d970
4.1.12026-02-20T18:43:17.930526ZCached167b590c7e8e2e1738dc92fcdee822ca4b8ad1c7b71738adad938bbb3d614da9
4.2.02026-02-23T12:00:38.847517ZCached9a868c4a13dae1a17df19e48e26ba5989ce3545ebf226abf35299fd12d70c5d6
4.3.02026-02-26T09:11:02.636611ZCached9892eb9051c8dc25bf63c49a34a058fe33fcf1503099cab4cd9015b5c760df4c
4.4.02026-02-27T08:08:16.390260ZCachedf2d9ac386a7bd4f5b8011cc4a6e77a2ef3bd18d42e3ef8463bdfb6057c3a0a93
4.5.02026-03-02T20:23:27.949795ZCached57e6434e016b318736d1f0e1316a1f204a6127d03a25af44218b643d20b4fc1c
4.6.02026-03-03T12:04:43.533531ZCached7708ac41f0e3692066d682e384d9a1a5b647c514a981982bf0c8522f5e8845ba
4.6.12026-03-04T11:12:13.036549ZCached4084b07d2db6f73861fd190a559d8afceb29a7ed750017108d93414957befcb4
4.6.22026-03-04T11:51:15.177423ZCachedc95695c9b4a2ace82f0a75789428a746bc2af789974a7b6b5e96e1bd0eb55301
4.6.32026-03-15T13:33:04.926599ZCachedee8e3631a9ad073daa803a45d4d638020f66c320a225567a0aed4b1ca20da1ae
4.6.42026-03-24T12:41:08.850749ZCachedd4701fbbdf508de831e847fd1d49c644ffa6191c6018c416ab6f66bbe1ad5fff
4.7.02026-06-10T14:27:24.992731ZCachedda6edbc8d5ae7361f680062795a8561c65622cbbd02bbd50b9291f188677bded
4.8.02026-06-17T09:05:38.128132ZCachedf755f124fd48da7a9f24e4ec825d6b11a60602e956ac9467e974e88554375181
4.9.02026-06-20T12:42:03.115888ZCached9c61372fecd5f6ac6aaa66fa6808b4c303b8574875e853a0916116bf2fae6840
4.9.12026-07-29T06:59:22.490010ZCached2d76205eb8cbe8acd8d000357017231bc2d3a056ccec5201d72a589808d9861b
4.10.02026-07-30T15:46:25.561343ZCached974594da61403c25849107ce6df0a31034f8a660612dc89c582b70caa03e6e98
4.10.12026-08-26T07:26:40.863967ZCached20d35070c8a9a767086d350608cda24a292bad7fbcc1c69e486e2c6125928084
4.11.02026-09-08T12:08:49.769141ZCachedbdbc622b5406f2773752d3ccdfa686567cbc914a99468760290c9e27817fbddd
4.11.12026-09-08T14:01:57.316025ZCached8fdd544f275dcbbdad3012fc497a15dd0a6984fc431130b16ac4929e19e542a8
4.12.02026-09-09T10:05:26.803084ZCached9165bb982487e899e4b35729e821e4e2d2407fb9b0c8d4e07d3ea39e148200ba
4.13.02026-09-26T17:58:05.220437ZCached139121951111a41fbd87a422e2915baf4dda8f7f91b3d2c2be2ee18f1bc2c17e
4.13.12026-09-28T11:33:02.069894ZCached8bc77b763c571dc3736cf00f011619315d06b917ae986a0a8191813e70c014b2

Dependencies

cached_network_image_platform_interface_ce "^5.2.0"

cached_network_image_web_ce "^2.1.0"

clock "^1.1.1"

crypto "^3.0.0"

file "\u003e=7.0.0 \u003c8.0.0"

flutter { "sdk": "flutter" }

hive_ce "^2.0.0"

http "^1.6.0"

meta "^1.9.0"

octo_image "^2.1.0"

path "^1.8.0"

path_provider "^2.0.0"