/// Album Details Page - Desktop Layout library; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/theme/colors.dart'; import '../../providers/album_provider.dart'; import '../../providers/music_provider.dart'; import '../../../domain/entities/track.dart'; import '../../widgets/album/album_widgets.dart'; import '../common/cached_network_image_with_fallback.dart'; class AlbumDesktopPage extends ConsumerStatefulWidget { final String albumId; const AlbumDesktopPage({ required this.albumId, super.key, }); @override ConsumerState createState() => _AlbumDesktopPageState(); } class _AlbumDesktopPageState extends ConsumerState { @override void initState() { super.initState(); // Load album data Future.microtask(() { ref.read(albumProvider.notifier).loadAlbum(widget.albumId); }); } @override Widget build(BuildContext context) { final albumState = ref.watch(albumProvider); if (albumState.isLoading && albumState.album == null) { return _buildLoadingState(); } if (albumState.error != null && albumState.album == null) { return _buildErrorState(albumState.error!); } if (albumState.album == null) { return _buildEmptyState(); } return Scaffold( backgroundColor: AppColors.primary, body: Row( children: [ // Left panel - Album art and info Expanded( flex: 4, child: _buildLeftPanel(albumState), ), const VerticalDivider(width: 1, color: AppColors.surfaceVariant), // Right panel - Tracklist Expanded( flex: 6, child: _buildRightPanel(albumState), ), ], ), ); } Widget _buildLoadingState() { return const Center( child: CircularProgressIndicator(color: AppColors.cyan), ); } Widget _buildErrorState(String error) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.error_outline, size: 64, color: AppColors.error, ), const SizedBox(height: 16), const Text( 'Something went wrong', style: TextStyle( fontSize: 18, color: AppColors.error, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), Text( error, style: const TextStyle( fontSize: 14, color: AppColors.muted, ), textAlign: TextAlign.center, ), const SizedBox(height: 24), ElevatedButton( onPressed: () { ref.read(albumProvider.notifier).loadAlbum(widget.albumId); }, child: const Text('Retry'), ), ], ), ), ); } Widget _buildEmptyState() { return const Center( child: CircularProgressIndicator(color: AppColors.cyan), ); } Widget _buildLeftPanel(albumState) { final album = albumState.album!; return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ AppColors.violet.withOpacity(0.2), AppColors.primary, ], ), ), child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Album art Hero( tag: 'album_art_${album.id}', child: Container( decoration: BoxDecoration( boxShadow: AppColors.cyanGlow, borderRadius: BorderRadius.circular(12), ), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: CachedNetworkImageWithFallback( imageUrl: album.imageUrl, fallbackIcon: Icons.album, progressColor: AppColors.cyan, width: 320, height: 320, fit: BoxFit.cover, ), ), ), ), const SizedBox(height: 32), // Album title Text( album.title, style: const TextStyle( fontSize: 32, fontWeight: FontWeight.bold, color: AppColors.onBackground, ), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), const SizedBox(height: 12), // Artist name and year if (album.artist != null) Text( '${album.artist!.name}${album.releaseDate != null ? ' • ${album.releaseDate!.year}' : ''}', style: const TextStyle( fontSize: 17, color: AppColors.onSurfaceVariant, fontWeight: FontWeight.w500, ), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), const SizedBox(height: 24), // Action buttons _buildActionButtons(albumState), const SizedBox(height: 24), // Album info chips _buildAlbumInfo(albumState), ], ), ), ), ); } Widget _buildActionButtons(albumState) { final tracks = albumState.tracks; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // Play All button SizedBox( width: 180, height: 52, child: ElevatedButton.icon( onPressed: tracks.isNotEmpty ? () { final albumNotifier = ref.read(albumProvider.notifier); final playerNotifier = ref.read(playerProvider.notifier); albumNotifier.playAll(playerNotifier); } : null, icon: const Icon(Icons.play_arrow, size: 26), label: const Text('Play All', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)), style: ElevatedButton.styleFrom( backgroundColor: AppColors.cyan, foregroundColor: AppColors.primary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(26), ), elevation: 0, ), ), ), const SizedBox(width: 16), // Shuffle button Container( decoration: BoxDecoration( shape: BoxShape.circle, gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppColors.violet, AppColors.rose], ), boxShadow: AppColors.violetGlow, ), child: IconButton( icon: const Icon(Icons.shuffle, size: 24), color: AppColors.onBackground, onPressed: tracks.isNotEmpty ? () { final albumNotifier = ref.read(albumProvider.notifier); final playerNotifier = ref.read(playerProvider.notifier); albumNotifier.shuffle(playerNotifier); } : null, iconSize: 52, ), ), ], ); } Widget _buildAlbumInfo(albumState) { final album = albumState.album!; final tracks = albumState.tracks; return Wrap( alignment: WrapAlignment.center, spacing: 12, runSpacing: 8, children: [ if (album.totalTracks > 0) _buildInfoChip('${album.totalTracks} ${album.totalTracks == 1 ? 'track' : 'tracks'}'), if (album.totalTracks > 0 && albumState.totalDuration > 0) _buildInfoChip(albumState.formattedTotalDuration), if (album.genre != null) _buildInfoChip(album.genre!, isGenre: true), ], ); } Widget _buildInfoChip(String text, {bool isGenre = false}) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: isGenre ? AppColors.violet.withOpacity(0.2) : AppColors.surfaceVariant, borderRadius: BorderRadius.circular(20), border: Border.all( color: isGenre ? AppColors.violet.withOpacity(0.5) : AppColors.cyan.withOpacity(0.2), ), ), child: Text( text, style: TextStyle( color: isGenre ? AppColors.violet : AppColors.onSurfaceVariant, fontSize: 13, fontWeight: FontWeight.w500, ), ), ); } Widget _buildRightPanel(albumState) { final tracks = albumState.tracks; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: AppColors.surface.withOpacity(0.5), border: Border( bottom: BorderSide( color: AppColors.surfaceVariant, width: 1, ), ), ), child: Row( children: [ IconButton( icon: const Icon(Icons.arrow_back, color: AppColors.onBackground), onPressed: () => Navigator.of(context).pop(), ), const SizedBox(width: 16), const Text( 'Tracklist', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: AppColors.onBackground, ), ), const Spacer(), if (tracks.isNotEmpty) Text( '${tracks.length} tracks', style: const TextStyle( fontSize: 14, color: AppColors.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), ], ), ), // Tracklist Expanded( child: tracks.isEmpty ? _buildEmptyTracklistState() : ListView.builder( padding: const EdgeInsets.symmetric(vertical: 16), itemCount: tracks.length, itemBuilder: (context, index) { final track = tracks[index]; return AlbumTrackTile( track: track, index: index, onTap: () => _playTrack(track, index), ); }, ), ), ], ); } Widget _buildEmptyTracklistState() { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.music_note, size: 64, color: AppColors.muted, ), SizedBox(height: 16), Text( 'No tracks available', style: TextStyle( fontSize: 16, color: AppColors.muted, ), ), ], ), ); } void _playTrack(Track track, int index) { final albumNotifier = ref.read(albumProvider.notifier); final playerNotifier = ref.read(playerProvider.notifier); albumNotifier.playTrack(playerNotifier, track); } }