/// Artist Details Page - Mobile Layout library; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/theme/colors.dart'; import '../../providers/artist_provider.dart'; import '../../providers/music_provider.dart'; import '../../../domain/entities/track.dart'; import '../../widgets/artist/artist_track_tile.dart'; import '../../widgets/artist/artist_album_card.dart'; class ArtistMobilePage extends ConsumerStatefulWidget { final String artistId; const ArtistMobilePage({ required this.artistId, super.key, }); @override ConsumerState createState() => _ArtistMobilePageState(); } class _ArtistMobilePageState extends ConsumerState { @override void initState() { super.initState(); // Load artist data Future.microtask(() { ref.read(artistProvider.notifier).loadAllArtistData(widget.artistId); }); } @override Widget build(BuildContext context) { final artistState = ref.watch(artistProvider); if (artistState.isLoading && artistState.artist == null) { return _buildLoadingState(); } if (artistState.error != null && artistState.artist == null) { return _buildErrorState(artistState.error!); } if (artistState.artist == null) { return _buildEmptyState(); } return CustomScrollView( slivers: [ // Hero header _buildHeroHeader(artistState.artist!), // Play All button _buildPlayAllButton(artistState.topTracks), // Popular tracks section if (artistState.topTracks.isNotEmpty) _buildPopularTracksSection(artistState.topTracks), // Albums section if (artistState.albums.isNotEmpty) _buildAlbumsSection(artistState.albums), // Related tracks section if (artistState.relatedTracks.isNotEmpty) _buildRelatedTracksSection(artistState.relatedTracks), // Bottom spacing const SliverToBoxAdapter( child: SizedBox(height: 100), ), ], ); } 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(artistProvider.notifier).loadAllArtistData(widget.artistId); }, child: const Text('Retry'), ), ], ), ), ); } Widget _buildEmptyState() { return const Center( child: CircularProgressIndicator(color: AppColors.cyan), ); } Widget _buildHeroHeader(artist) { return SliverToBoxAdapter( child: Stack( children: [ // Background gradient Container( height: 280, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ AppColors.violet.withOpacity(0.3), AppColors.primary, ], ), ), ), // Artist image if (artist.imageUrl != null) Positioned.fill( child: Opacity( opacity: 0.2, child: Image.network( artist.imageUrl!, fit: BoxFit.cover, ), ), ), // Content SizedBox( height: 280, child: SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Back button IconButton( icon: const Icon(Icons.arrow_back, color: AppColors.onBackground), onPressed: () => Navigator.of(context).pop(), ), const Spacer(), // Artist image if (artist.imageUrl != null) Center( child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.network( artist.imageUrl!, width: 160, height: 160, fit: BoxFit.cover, ), ), ), const SizedBox(height: 16), // Artist name Text( artist.name, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: AppColors.onBackground, ), textAlign: TextAlign.center, ), const SizedBox(height: 8), // Genres if (artist.genres.isNotEmpty) Wrap( spacing: 8, alignment: WrapAlignment.center, children: artist.genres.take(3).map((genre) { return Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: AppColors.surfaceVariant, borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.cyan.withOpacity(0.3), ), ), child: Text( genre, style: const TextStyle( color: AppColors.cyan, fontSize: 12, fontWeight: FontWeight.w500, ), ), ); }).toList(), ), const SizedBox(height: 16), ], ), ), ), ), ], ), ); } Widget _buildPlayAllButton(List tracks) { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: tracks.isNotEmpty ? () { final playerNotifier = ref.read(playerProvider.notifier); playerNotifier.setQueue(tracks, startIndex: 0); playerNotifier.loadTrack(tracks.first); playerNotifier.play(); } : null, icon: const Icon(Icons.play_arrow, size: 28), label: const Text('Play All', style: TextStyle(fontSize: 16)), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), backgroundColor: AppColors.cyan, foregroundColor: AppColors.primary, ), ), ), ), ); } Widget _buildPopularTracksSection(List tracks) { return SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.fromLTRB(16, 24, 16, 12), child: Text( 'Popular', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: AppColors.onBackground, ), ), ), ...tracks.asMap().entries.map((entry) { final index = entry.key; final track = entry.value; return ArtistTrackTile( track: track, index: index, onTap: () => _playTrack(track), ); }), const SizedBox(height: 24), ], ), ); } Widget _buildAlbumsSection(albums) { return SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.fromLTRB(16, 24, 16, 12), child: Text( 'Albums', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: AppColors.onBackground, ), ), ), SizedBox( height: 200, child: ListView.builder( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 8), itemCount: albums.length, itemBuilder: (context, index) { return ArtistAlbumCard( album: albums[index], onTap: () { // TODO: Navigate to album details }, ); }, ), ), const SizedBox(height: 24), ], ), ); } Widget _buildRelatedTracksSection(List tracks) { return SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.fromLTRB(16, 24, 16, 12), child: Text( 'Related Tracks', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: AppColors.onBackground, ), ), ), ...tracks.asMap().entries.map((entry) { final index = entry.key; final track = entry.value; return ArtistTrackTile( track: track, index: index, onTap: () => _playTrack(track), ); }), ], ), ); } void _playTrack(Track track) { final playerNotifier = ref.read(playerProvider.notifier); final artistState = ref.read(artistProvider); playerNotifier.setQueue(artistState.topTracks, startIndex: 0); playerNotifier.loadTrack(track); playerNotifier.play(); } }