diff --git a/lib/modules/library/widgets/library_gridview_widget.dart b/lib/modules/library/widgets/library_gridview_widget.dart index ad12b35d..666c26d7 100644 --- a/lib/modules/library/widgets/library_gridview_widget.dart +++ b/lib/modules/library/widgets/library_gridview_widget.dart @@ -7,7 +7,7 @@ import 'package:mangayomi/modules/library/providers/library_state_provider.dart' import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/modules/library/widgets/continue_reader_button.dart'; import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; -import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; +import 'package:mangayomi/utils/cached_network.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; import 'package:mangayomi/utils/constant.dart'; import 'package:mangayomi/utils/headers.dart'; @@ -77,7 +77,7 @@ class _LibraryGridViewWidgetState extends State { image: entry.customCoverImage != null ? MemoryImage(entry.customCoverImage as Uint8List) as ImageProvider - : CustomExtendedNetworkImageProvider( + : coverProvider( toImgUrl( entry.customCoverFromTracker ?? entry.imageUrl ?? diff --git a/lib/modules/library/widgets/library_listview_widget.dart b/lib/modules/library/widgets/library_listview_widget.dart index 006df9b9..eb34bc7d 100644 --- a/lib/modules/library/widgets/library_listview_widget.dart +++ b/lib/modules/library/widgets/library_listview_widget.dart @@ -7,7 +7,7 @@ import 'package:mangayomi/modules/library/providers/library_state_provider.dart' import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/modules/library/widgets/continue_reader_button.dart'; import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; -import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; +import 'package:mangayomi/utils/cached_network.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; import 'package:mangayomi/utils/constant.dart'; import 'package:mangayomi/utils/headers.dart'; @@ -127,7 +127,7 @@ class LibraryListViewWidget extends StatelessWidget { as Uint8List, ) as ImageProvider - : CustomExtendedNetworkImageProvider( + : coverProvider( toImgUrl( entry.customCoverFromTracker ?? entry.imageUrl!, diff --git a/lib/modules/widgets/manga_image_card_widget.dart b/lib/modules/widgets/manga_image_card_widget.dart index 166c253c..feb817b0 100644 --- a/lib/modules/widgets/manga_image_card_widget.dart +++ b/lib/modules/widgets/manga_image_card_widget.dart @@ -9,7 +9,7 @@ import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/models/settings.dart'; import 'package:mangayomi/models/source.dart'; import 'package:mangayomi/modules/manga/detail/manga_detail_main.dart'; -import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; +import 'package:mangayomi/utils/cached_network.dart'; import 'package:mangayomi/router/router.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; import 'package:mangayomi/utils/constant.dart'; @@ -46,7 +46,7 @@ class MangaImageCardWidget extends ConsumerWidget { image: hasData && libraryManga!.customCoverImage != null ? MemoryImage(libraryManga!.customCoverImage as Uint8List) as ImageProvider - : CustomExtendedNetworkImageProvider( + : coverProvider( toImgUrl( hasData ? libraryManga!.customCoverFromTracker ?? @@ -61,7 +61,6 @@ class MangaImageCardWidget extends ConsumerWidget { sourceId: source.id, ), ), - cache: true, cacheMaxAge: const Duration(days: 7), ), onTap: () => pushToMangaReaderDetail( @@ -151,7 +150,7 @@ class MangaImageCardListTileWidget extends ConsumerWidget { final image = hasData && libraryManga!.customCoverImage != null ? MemoryImage(libraryManga!.customCoverImage as Uint8List) as ImageProvider - : CustomExtendedNetworkImageProvider( + : coverProvider( toImgUrl( hasData ? libraryManga!.customCoverFromTracker ?? diff --git a/lib/utils/cached_network.dart b/lib/utils/cached_network.dart index c518fffb..43229435 100644 --- a/lib/utils/cached_network.dart +++ b/lib/utils/cached_network.dart @@ -2,6 +2,47 @@ import 'package:extended_image/extended_image.dart'; import 'package:flutter/material.dart'; import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; +/// Default upper bound on the encoded byte size of a thumbnail-sized cover +/// after `ExtendedResizeImage` resamples. 200 KB roughly maps to a 400x600 +/// JPEG / WebP — sharp enough at typical grid / list display sizes on +/// high-DPR screens, while keeping the *decoded* RAM footprint small (the +/// resampled image goes into Flutter's `imageCache` decoded, where every +/// saved KB matters). +const int _coverMaxBytes = 200 << 10; + +/// Returns an `ImageProvider` for a manga / anime cover URL that decodes at +/// thumbnail resolution rather than the source resolution. +/// +/// Source covers are commonly 720x1080 or larger (~3 MB decoded RGBA per +/// cover). When used directly in a library grid or list, every visible +/// thumbnail fills `imageCache` with a 3 MB blob even though it renders at +/// ~150x220 logical pixels. Wrapping the provider in `ExtendedResizeImage` +/// instructs the decoder to resample to a much smaller bitmap before it +/// hits the cache, so the same in-memory budget holds far more thumbnails +/// and scrolling stops thrashing. +/// +/// Use this for thumbnail call sites (library grid / list, browse search +/// cards, tracker results, calendar, etc.). Do *not* use it for large hero +/// covers (manga detail page) or for reader pages, which need full +/// resolution. +ImageProvider coverProvider( + String url, { + Map? headers, + int maxBytes = _coverMaxBytes, + bool cache = true, + Duration? cacheMaxAge, +}) { + return ExtendedResizeImage( + CustomExtendedNetworkImageProvider( + url, + headers: headers, + cache: cache, + cacheMaxAge: cacheMaxAge, + ), + maxBytes: maxBytes, + ); +} + Widget cachedNetworkImage({ Map? headers, required String imageUrl,