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
@@ -1,3 +1,4 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -204,9 +205,14 @@ class _ResultTile extends StatelessWidget {
color: fs.slate,
child: row.imageUrl.isEmpty
? Icon(Icons.album, color: fs.ash)
: Image.network(row.imageUrl, fit: BoxFit.cover,
errorBuilder: (_, __, ___) =>
Icon(Icons.album, color: fs.ash)),
: CachedNetworkImage(
imageUrl: row.imageUrl,
fit: BoxFit.cover,
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
errorWidget: (_, __, ___) =>
Icon(Icons.album, color: fs.ash),
),
),
),
const SizedBox(width: 12),