flutter_cached_network_image icon indicating copy to clipboard operation
flutter_cached_network_image copied to clipboard

If loading an image fails with maxHeightDiskCache specified, errorWidget will not be called next time.

Open YuyaKoshimizu opened this issue 1 year ago • 4 comments

🐛 Bug Report

As the title says, if CachedNetworkImage with maxHeightDiskCache (or maxWidthDiskCache) fails to load an image (403, etc.), errorWidget is called the first time, but not the next time. I think it is probably due to the fact that the image that failed to load is cached, but is there a solution? I would like to add that I have tried deleting the cache, but it is not possible to do so. Thanks for the answer.

Expected behavior

Images that fail to load are not cached.

Reproduction steps

return CachedNetworkImage(
  imageUrl: path, // Path of the image that results in a 403 error
  width: 128,
  height: 128,
  maxWidthDiskCache: 128,
  maxHeightDiskCache: 128,
  errorWidget: (context, url, error) {
    // First time through, but after that it goes through.
    return const SizedBox.shrink();
  },
  placeholder: (context, url) {
    // errorWidget is not called, so it is here to stay
    return const CircularProgressIndicator();
  },
  imageBuilder: (context, imageProvider) {
    // omission
  },
  errorListener: (value) async {
    // It tries to delete the cache, but it is always false.
    final result = CachedNetworkImage.evictFromCache(path);
    print('delete cache: $result'); // delete cache: false
  },
);

Configuration

Version:

  • Flutter: 3.24.3
  • cached_network_image: 3.4.1

Platform:

  • [x] :iphone: iOS
  • [x] :robot: Android

YuyaKoshimizu avatar Oct 18 '24 08:10 YuyaKoshimizu

Same problem here.

Mine happens in

  • Flutter 3.24.3
  • cache_nework_image: 3.3.1

Edit: If I don't specify both maxHeightDiskCache and maxWidthDiskCache values to CacheNetworkImage, it works fine as expected.

Sovann72 avatar Oct 29 '24 11:10 Sovann72

+1 any solution?

u-sour avatar Dec 06 '24 03:12 u-sour

It happens for me too, I tried to delete using DefaultCacheManager() but it doesn't work.

MirceaX2Mobile avatar Dec 26 '24 20:12 MirceaX2Mobile

The problem is here in image_cache_manager.dart

line 47

    var runningResize = _runningResizes[resizedKey];
    if (runningResize == null) {
      runningResize = _fetchedResizedFile(
        url,
        key,
        resizedKey,
        headers,
        withProgress,
        maxWidth: maxWidth,
        maxHeight: maxHeight,
      ).asBroadcastStream();
      _runningResizes[resizedKey] = runningResize;
    }
    yield* runningResize;
    _runningResizes.remove(resizedKey);

The last failed runningResize is cached here in this List, I guess it should be removed when an exception occurs.

So that's why the next time when you try to the load the Image, _fetchedResizedFile it doesn't even get called because there is an if (runningResize == null) which is false because we are retaining the last failed file.

A fix should be something for line 74

  Future<FileInfo> _resizeImageFile(
    FileInfo originalFile,
    String key,
    int? maxWidth,
    int? maxHeight,
  ) async {
    final originalFileName = originalFile.file.path;
    final fileExtension = originalFileName.split('.').last;
    if (!supportedFileNames.contains(fileExtension)) {
      _runningResizes.remove(key);
      return originalFile;
    }

Adding _runningResizes.remove(key), before returning the original file.

Other fix

  • Create a clean up function for _runningResizes list on ImageCacheManager
  • Then in _image_loader.dart

At line 132 add this code after evicting the image

      scheduleMicrotask(() {
        evictImage();
        if (cacheManager is ImageCacheManager) {
          cacheManager.cleanUpResizedImages();
        }
      });

@renefloor maybe you can take a look

MirceaX2Mobile avatar Dec 26 '24 21:12 MirceaX2Mobile