feat(flutter): disk-persistent cover cache via cached_network_image

Slice 1 of the cover-caching pass. The previous Image.network /
NetworkImage path only cached covers in memory, so a scroll-off + scroll-
back or an app restart re-downloaded every tile from the server. Swap
to cached_network_image so bytes land on disk (path_provider temp dir,
URL-keyed) and survive both.

Sites migrated:
  - ServerImage (all /api/*/cover usage — home grid, library, playlist,
    artist/album detail headers)
  - DiscoverScreen Lidarr suggestion thumbnails
  - PlayerBar mini cover (HTTPS branch; file:// branch unchanged since
    AlbumCoverCache files are already on disk)

Auth header forwarding preserved via httpHeaders. Fade-in disabled so
populated grids paint instantly on cache hit.

Slice 2 (pre-warm during sync) builds on this same cache manager.
This commit is contained in:
2026-05-13 17:59:42 -04:00
parent ae5de91006
commit f732c49645
5 changed files with 81 additions and 18 deletions
+7 -1
View File
@@ -1,6 +1,7 @@
import 'dart:io';
import 'package:audio_service/audio_service.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -96,10 +97,15 @@ class _TrackInfo extends StatelessWidget {
// so the transition works regardless of what's playing.
Widget cover;
if (media.artUri != null) {
// CachedNetworkImageProvider for HTTPS art URIs so the mini bar
// hits the same disk cache the rest of the UI uses (ServerImage,
// discover thumbnails). FileImage stays for the file:// branch
// populated by AlbumCoverCache — bytes are already on disk and a
// second cache layer would only burn duplicate space.
cover = Image(
image: media.artUri!.isScheme('file')
? FileImage(File.fromUri(media.artUri!)) as ImageProvider
: NetworkImage(media.artUri.toString()),
: CachedNetworkImageProvider(media.artUri.toString()),
width: 48,
height: 48,
// Without a fit, Image paints the source at its intrinsic