import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../app/router.dart'; import '../../app/theme.dart'; import '../../audio/player_providers.dart'; import '../../core/models.dart'; import '../../offline/download_store.dart'; import '../../shared/widgets/artwork.dart'; import '../../shared/widgets/catalog_builder.dart'; import '../../shared/widgets/mini_player.dart'; import '../../shared/widgets/section_header.dart'; import '../../shared/widgets/track_tile.dart'; class AlbumScreen extends ConsumerWidget { const AlbumScreen({super.key, required this.albumId}); final String albumId; @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( bottomNavigationBar: const SafeArea(child: MiniPlayer()), body: CatalogBuilder(builder: (catalog) { final album = catalog.albumsById[albumId]; if (album == null) { return const Center( child: EmptyState( icon: Icons.album_outlined, title: 'Album not found', message: 'It may have been removed.', ), ); } final tracks = catalog.byAlbum(albumId); final total = tracks.fold(Duration.zero, (sum, t) => sum + t.duration); final controller = ref.read(playerControllerProvider); return CustomScrollView( slivers: [ const SliverAppBar(pinned: true), SliverToBoxAdapter( child: Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(12), child: SizedBox.square(dimension: 200, child: AlbumCover(album: album, size: 200)), ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text(album.title, textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineSmall), ), const SizedBox(height: 4), Text( [ if (album.year != null) '${album.year}', '${tracks.length} songs', '${total.inMinutes} min', ].join(' ยท '), style: const TextStyle(color: NkColors.muted), ), ], ), ), SliverPadding( padding: const EdgeInsets.fromLTRB(18, 12, 18, 8), sliver: SliverToBoxAdapter( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _DownloadAlbumButton(tracks: tracks), const SizedBox(width: 8), IconButton( tooltip: 'Shuffle', icon: const Icon(Icons.shuffle, color: NkColors.sun), onPressed: tracks.isEmpty ? null : () => controller.playTracks(tracks, shuffle: true), ), const SizedBox(width: 16), IconButton.filled( tooltip: 'Play all', style: IconButton.styleFrom(fixedSize: const Size(56, 56)), onPressed: tracks.isEmpty ? null : () => controller.playTracks(tracks), icon: const Icon(Icons.play_arrow_rounded, size: 30), ), ], ), ), ), SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 18), sliver: SliverList.builder( itemCount: tracks.length, itemBuilder: (_, i) => TrackTile(track: tracks[i], onTap: () => controller.playTracks(tracks, index: i)), ), ), const SliverToBoxAdapter(child: SizedBox(height: 24)), ], ); }), ); } } class _DownloadAlbumButton extends ConsumerWidget { const _DownloadAlbumButton({required this.tracks}); final List tracks; @override Widget build(BuildContext context, WidgetRef ref) { final d = ref.watch(downloadsProvider); final done = tracks.where((t) => d.isDownloaded(t.id)).length; final busy = tracks.any((t) => d.progress.containsKey(t.id)); final all = tracks.isNotEmpty && done == tracks.length; return IconButton( tooltip: all ? 'Downloaded' : 'Download album', icon: busy ? const SizedBox.square(dimension: 22, child: CircularProgressIndicator(strokeWidth: 2.5)) : Icon(all ? Icons.download_done_rounded : Icons.download_rounded, color: all ? NkColors.teaLeaf : NkColors.cream), onPressed: all || busy || tracks.isEmpty ? null : () { if (!isSignedIn) { context.push('/login?mode=signup'); return; } final messenger = ScaffoldMessenger.of(context); ref.read(playerControllerProvider).download(tracks).then((problem) => messenger.showSnackBar( SnackBar( content: Text(problem ?? 'Downloading ${tracks.length - done} songs. Find them in Downloads.'), ), )); }, ); } } /// Square album card for horizontal rows and grids. class AlbumCard extends StatelessWidget { const AlbumCard({super.key, required this.album, required this.songCount, required this.onTap, this.size = 140}); final Album album; final int songCount; final VoidCallback onTap; final double size; @override Widget build(BuildContext context) { return InkWell( borderRadius: BorderRadius.circular(10), onTap: onTap, child: SizedBox( width: size, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(10), child: SizedBox.square(dimension: size, child: AlbumCover(album: album, size: size)), ), const SizedBox(height: 6), Text(album.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w600)), Text('$songCount songs', style: const TextStyle(color: NkColors.muted, fontSize: 12)), ], ), ), ); } }