fix(ui): restore plain loading skeleton on extension album screen

The hero-era loading machinery survived the hero-flight revert: the
loading scaffold rendered the real cover, _RouteSettled deferred the
swap until the push transition ended, and SkeletonCrossfade faded
between the two. The loaded header decodes the cover under a different
mem-cache key (viewport-sized vs full-res), so opening an album from
the artist screen flashed the cover: shown, blanked, shown again.

Restore the pre-hero flow: shimmer skeleton while loading, hard cut to
AlbumScreen so the cover appears exactly once. Removes the now-unused
_AlbumLoadingScaffold and three imports that only existed for it.
This commit is contained in:
zarzet
2026-07-21 07:35:03 +07:00
parent b7e96facbe
commit bd6e2b4472
2 changed files with 21 additions and 98 deletions
-3
View File
@@ -17,9 +17,6 @@ import 'package:spotiflac_android/providers/local_library_provider.dart';
import 'package:spotiflac_android/screens/track_metadata_screen.dart';
import 'package:spotiflac_android/screens/album_screen.dart';
import 'package:spotiflac_android/screens/artist_screen.dart';
import 'package:spotiflac_android/screens/collapsing_header_scroll_mixin.dart';
import 'package:spotiflac_android/utils/cover_art_utils.dart';
import 'package:spotiflac_android/widgets/album_detail_header.dart';
import 'package:spotiflac_android/services/csv_import_service.dart';
import 'package:spotiflac_android/services/downloaded_embedded_cover_resolver.dart';
import 'package:spotiflac_android/services/platform_bridge.dart';
+21 -95
View File
@@ -751,69 +751,6 @@ mixin _RouteSettled<T extends StatefulWidget> on State<T> {
}
}
/// Loading state for [ExtensionAlbumScreen]: the real collection header
/// (title + cover already in their final slots) above a shimmering track
/// list, so arriving content barely shifts the page.
class _AlbumLoadingScaffold extends StatefulWidget {
final String title;
final String? coverUrl;
const _AlbumLoadingScaffold({required this.title, required this.coverUrl});
@override
State<_AlbumLoadingScaffold> createState() => _AlbumLoadingScaffoldState();
}
class _AlbumLoadingScaffoldState extends State<_AlbumLoadingScaffold>
with CollapsingHeaderScrollMixin<_AlbumLoadingScaffold> {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final url = widget.coverUrl;
Widget cover() => url != null && url.isNotEmpty
? CachedCoverImage(
imageUrl: highResCoverUrl(url) ?? url,
fit: BoxFit.cover,
)
: Container(
color: colorScheme.surfaceContainerHighest,
child: Icon(
Icons.album,
size: 48,
color: colorScheme.onSurfaceVariant,
),
);
return Scaffold(
body: CustomScrollView(
controller: scrollController,
slivers: [
AlbumDetailHeader(
title: widget.title,
expandedHeight: calculateExpandedHeight(context),
showTitleInAppBar: showTitleInAppBar,
background: cover(),
coverBuilder: (context, coverSize) => cover(),
// Placeholder rows sized like the loaded header's subtitle
// (16pt artist text ≈ 22px), meta (13pt row ≈ 18px), and 48px
// action row, so the cover doesn't shift when content lands.
subtitle: const ShimmerLoading(
child: SkeletonBox(width: 120, height: 22, borderRadius: 4),
),
meta: const ShimmerLoading(
child: SkeletonBox(width: 150, height: 18, borderRadius: 6),
),
actions: const ShimmerLoading(
child: SkeletonBox(width: 220, height: 48, borderRadius: 24),
),
),
const SliverToBoxAdapter(child: AlbumTrackListSkeleton(itemCount: 8)),
],
),
);
}
}
/// Loading state for [ExtensionArtistScreen]: the real full-bleed header with
/// the cover and artist name above the discography skeleton.
class _ArtistLoadingScaffold extends StatelessWidget {
@@ -952,8 +889,7 @@ class ExtensionAlbumScreen extends ConsumerStatefulWidget {
_ExtensionAlbumScreenState();
}
class _ExtensionAlbumScreenState extends ConsumerState<ExtensionAlbumScreen>
with _RouteSettled<ExtensionAlbumScreen> {
class _ExtensionAlbumScreenState extends ConsumerState<ExtensionAlbumScreen> {
List<Track>? _tracks;
bool _isLoading = true;
String? _error;
@@ -1086,40 +1022,30 @@ class _ExtensionAlbumScreenState extends ConsumerState<ExtensionAlbumScreen>
@override
Widget build(BuildContext context) {
if (_error != null) {
return SkeletonCrossfade(
child: _LoadingOrErrorScaffold(
title: widget.albumName,
isLoading: false,
error: _error,
loadingBody: const SizedBox.shrink(),
onRetry: _fetchTracks,
if (_isLoading || _error != null) {
return _LoadingOrErrorScaffold(
title: widget.albumName,
isLoading: _isLoading,
error: _error,
loadingBody: const AlbumTrackListSkeleton(
itemCount: 10,
showCoverHeader: true,
),
onRetry: _fetchTracks,
);
}
if (_isLoading || !routeSettled) {
return SkeletonCrossfade(
child: _AlbumLoadingScaffold(
title: widget.albumName,
coverUrl: widget.coverUrl,
),
);
}
return SkeletonCrossfade(
child: AlbumScreen(
albumId: widget.albumId,
albumName: widget.albumName,
coverUrl: widget.coverUrl,
headerVideoUrl: _headerVideoUrl,
headerImageUrl: _headerImageUrl,
audioTraits: _audioTraits,
tracks: _tracks,
extensionId: widget.extensionId,
artistId: _artistId,
artistName: _artistName,
),
return AlbumScreen(
albumId: widget.albumId,
albumName: widget.albumName,
coverUrl: widget.coverUrl,
headerVideoUrl: _headerVideoUrl,
headerImageUrl: _headerImageUrl,
audioTraits: _audioTraits,
tracks: _tracks,
extensionId: widget.extensionId,
artistId: _artistId,
artistName: _artistName,
);
}
}