import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:immich_mobile/service_locator.dart'; import 'package:immich_mobile/utils/immich_api_client.dart'; /// An exception for the [ImageLoader] and the Immich image providers class ImageLoadingException implements Exception { final String message; const ImageLoadingException(this.message); @override String toString() => 'ImageLoadingException: $message'; } /// Loads the codec from the URI and sends the events to the [chunkEvents] stream /// /// Credit to [flutter_cached_network_image](https://github.com/Baseflow/flutter_cached_network_image/blob/develop/cached_network_image/lib/src/image_provider/_image_loader.dart) /// for this wonderful implementation of their image loader abstract final class ImageLoader { static Future loadImageFromCache( String uri, { required CacheManager cache, required ImageDecoderCallback decode, StreamController? chunkEvents, }) async { final stream = cache.getFileStream( uri, headers: di().headers, withProgress: chunkEvents != null, ); await for (final result in stream) { if (result is DownloadProgress) { // We are downloading the file, so update the [chunkEvents] chunkEvents?.add( ImageChunkEvent( cumulativeBytesLoaded: result.downloaded, expectedTotalBytes: result.totalSize, ), ); } else if (result is FileInfo) { // We have the file final buffer = await ui.ImmutableBuffer.fromFilePath(result.file.path); return await decode(buffer); } } // If we get here, the image failed to load from the cache stream throw const ImageLoadingException('Could not load image from stream'); } }