From d39e7da10d49bc3edafd7cf0360608f68a6cf6cc Mon Sep 17 00:00:00 2001 From: Luis Nachtigall <31982496+LeLunZ@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:28:55 +0200 Subject: [PATCH] fix(mobile): fix flutter cache eviction on thumbnails (#27663) * fix: add markFinished parameter to loadRequest and loadCodecRequest methods * update loadRequest and loadCodecRequest methods to use isFinal * Apply suggestions from code review Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com> * remove redundant check * fix: ensure isFinished is set correctly during cache eviction * formatting --------- Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com> --- .../widgets/images/image_provider.dart | 30 ++++++++----------- .../widgets/images/local_image_provider.dart | 16 +++++----- .../widgets/images/remote_image_provider.dart | 13 ++++---- .../widgets/images/thumb_hash_provider.dart | 2 +- 4 files changed, 26 insertions(+), 35 deletions(-) diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index 1d87fa404e..47ebd37014 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -51,7 +51,7 @@ mixin CancellableImageProviderMixin on CancellableImageProvide return null; } - Stream loadRequest(ImageRequest request, ImageDecoderCallback decode, {bool evictOnError = true}) async* { + Stream loadRequest(ImageRequest request, ImageDecoderCallback decode, {required bool isFinal}) async* { if (isCancelled) { this.request = null; return; @@ -59,21 +59,18 @@ mixin CancellableImageProviderMixin on CancellableImageProvide try { final image = await request.load(decode); - if (isCancelled) { - return; - } - if (image == null && evictOnError) { - PaintingBinding.instance.imageCache.evict(this); - return; - } else if (image == null) { + if (isCancelled || image == null) { + image?.dispose(); return; } + isFinished = isFinal; yield image; } catch (e, stack) { if (isCancelled) { return; } - if (evictOnError) { + if (isFinal) { + isFinished = true; PaintingBinding.instance.imageCache.evict(this); rethrow; } @@ -83,7 +80,7 @@ mixin CancellableImageProviderMixin on CancellableImageProvide } } - Future loadCodecRequest(ImageRequest request) async { + Future loadCodecRequest(ImageRequest request, {required bool isFinal}) async { if (isCancelled) { this.request = null; return null; @@ -91,20 +88,19 @@ mixin CancellableImageProviderMixin on CancellableImageProvide try { final codec = await request.loadCodec(); - if (isCancelled) { + if (isCancelled || codec == null) { codec?.dispose(); return null; } - if (codec == null) { - PaintingBinding.instance.imageCache.evict(this); - return null; - } + isFinished = isFinal; return codec; } catch (e) { - if (!isCancelled) { + if (isFinal) { + isFinished = true; PaintingBinding.instance.imageCache.evict(this); + rethrow; } - rethrow; + return null; } finally { this.request = null; } diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index d4f829bed5..d29a1cd56d 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -36,7 +36,7 @@ class LocalThumbProvider extends CancellableImageProvider Stream _codec(LocalThumbProvider key, ImageDecoderCallback decode) { final request = this.request = LocalImageRequest(localId: key.id, size: key.size, assetType: key.assetType); - return loadRequest(request, decode); + return loadRequest(request, decode, isFinal: true); } @override @@ -103,16 +103,16 @@ class LocalFullImageProvider extends CancellableImageProvider _animatedCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async* { @@ -139,7 +138,7 @@ class LocalFullImageProvider extends CancellableImageProvider Stream _codec(RemoteImageProvider key, ImageDecoderCallback decode) { final request = this.request = RemoteImageRequest(uri: key.url); - return loadRequest(request, decode); + return loadRequest(request, decode, isFinal: true); } @override @@ -112,10 +112,9 @@ class RemoteFullImageProvider extends CancellableImageProvider _animatedCodec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* { @@ -138,7 +136,7 @@ class RemoteFullImageProvider extends CancellableImageProvider Stream _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) { final request = this.request = ThumbhashImageRequest(thumbhash: key.thumbHash); - return loadRequest(request, decode); + return loadRequest(request, decode, isFinal: true); } @override