/// Library Page - Mobile Layout library; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/theme/colors.dart'; import '../../providers/library_provider.dart'; import '../../widgets/library/playlist_tile.dart'; import '../../../domain/entities/track.dart'; import '../../../domain/entities/album.dart'; import '../../../domain/entities/artist.dart'; class LibraryMobilePage extends ConsumerStatefulWidget { const LibraryMobilePage({super.key}); @override ConsumerState createState() => _LibraryMobilePageState(); } class _LibraryMobilePageState extends ConsumerState with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 4, vsync: this); // Load library on init WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(libraryProvider.notifier).loadLibrary(); }); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final libraryState = ref.watch(libraryProvider); return Column( children: [ // Header with title _buildHeader(libraryState), // Tab bar _buildTabBar(), // Content based on selected tab Expanded( child: _buildContent(libraryState), ), ], ); } Widget _buildHeader(dynamic libraryState) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: AppColors.surfaceVariant.withOpacity(0.5), border: Border( bottom: BorderSide( color: AppColors.cyan.withOpacity(0.2), width: 1, ), ), ), child: Row( children: [ const Icon( Icons.library_music, color: AppColors.cyan, size: 24, ), const SizedBox(width: 12), const Text( 'Your Library', style: TextStyle( color: AppColors.onSurface, fontSize: 20, fontWeight: FontWeight.w600, ), ), const Spacer(), if (libraryState.totalItems > 0) IconButton( icon: const Icon(Icons.refresh, color: AppColors.cyan), onPressed: () { ref.read(libraryProvider.notifier).refresh(); }, tooltip: 'Refresh', iconSize: 20, ), ], ), ); } Widget _buildTabBar() { return Container( decoration: BoxDecoration( color: AppColors.surface, border: Border( bottom: BorderSide( color: AppColors.cyan.withOpacity(0.2), width: 1, ), ), ), child: TabBar( controller: _tabController, indicatorColor: AppColors.cyan, labelColor: AppColors.cyan, unselectedLabelColor: AppColors.muted, labelStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), tabs: const [ Tab(text: 'Playlists'), Tab(text: 'Songs'), Tab(text: 'Albums'), Tab(text: 'Artists'), ], ), ); } Widget _buildContent(dynamic libraryState) { if (libraryState.isLoading) { return const Center( child: CircularProgressIndicator(color: AppColors.cyan), ); } if (libraryState.error != null) { return _buildErrorState(libraryState.error ?? 'Unknown error'); } return TabBarView( controller: _tabController, children: [ _buildPlaylistsTab(libraryState.playlists), _buildLikedSongsTab(libraryState.likedSongs), _buildAlbumsTab(libraryState.savedAlbums), _buildArtistsTab(libraryState.followedArtists), ], ); } Widget _buildPlaylistsTab(List playlists) { if (playlists.isEmpty) { return _buildEmptyState( icon: Icons.playlist_play, message: 'No playlists yet', submessage: 'Create your first playlist to get started', ); } return ListView.builder( padding: const EdgeInsets.all(16), itemCount: playlists.length, itemBuilder: (context, index) { final playlist = playlists[index]; return Padding( padding: const EdgeInsets.only(bottom: 12), child: PlaylistTile( playlist: playlist, onTap: () => _openPlaylist(playlist), canDelete: true, onDelete: () => _confirmDeletePlaylist(playlist), ), ); }, ); } Widget _buildLikedSongsTab(List likedSongs) { if (likedSongs.isEmpty) { return _buildEmptyState( icon: Icons.favorite_border, message: 'No liked songs', submessage: 'Like songs to see them here', ); } return ListView.builder( padding: const EdgeInsets.all(16), itemCount: likedSongs.length, itemBuilder: (context, index) { final track = likedSongs[index] as Track; return _buildTrackTile(track, index); }, ); } Widget _buildAlbumsTab(List albums) { if (albums.isEmpty) { return _buildEmptyState( icon: Icons.album, message: 'No saved albums', submessage: 'Save albums to see them here', ); } return GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1, ), itemCount: albums.length, itemBuilder: (context, index) { final album = albums[index] as Album; return _buildAlbumCard(album); }, ); } Widget _buildArtistsTab(List artists) { if (artists.isEmpty) { return _buildEmptyState( icon: Icons.person, message: 'No followed artists', submessage: 'Follow artists to see them here', ); } return GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1, ), itemCount: artists.length, itemBuilder: (context, index) { final artist = artists[index] as Artist; return _buildArtistCard(artist); }, ); } Widget _buildTrackTile(Track track, int index) { return Container( margin: const EdgeInsets.only(bottom: 8), decoration: BoxDecoration( color: AppColors.surfaceVariant, borderRadius: BorderRadius.circular(8), border: Border.all( color: AppColors.cyan.withOpacity(0.1), ), ), child: ListTile( leading: Text( '${index + 1}', style: const TextStyle( color: AppColors.muted, fontSize: 14, fontWeight: FontWeight.w500, ), ), title: Text( track.title, style: const TextStyle( color: AppColors.onSurface, fontWeight: FontWeight.w500, fontSize: 14, ), ), subtitle: Text( track.artist?.name ?? 'Unknown Artist', style: const TextStyle( color: AppColors.muted, fontSize: 12, ), ), trailing: Text( track.formattedDuration, style: const TextStyle( color: AppColors.muted, fontSize: 12, ), ), onTap: () { // TODO: Play track }, ), ); } Widget _buildAlbumCard(Album album) { return GestureDetector( onTap: () => _openAlbum(album), child: Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), border: Border.all( color: AppColors.rose.withOpacity(0.3), ), ), child: Column( children: [ Expanded( child: Container( width: double.infinity, decoration: BoxDecoration( gradient: AppColors.accentGradient, borderRadius: const BorderRadius.vertical( top: Radius.circular(12), ), ), child: album.imageUrl != null ? ClipRRect( borderRadius: const BorderRadius.vertical( top: Radius.circular(12), ), child: Image.network( album.imageUrl!, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.album, size: 48, color: AppColors.muted, ); }, ), ) : const Icon( Icons.album, size: 48, color: AppColors.muted, ), ), ), Padding( padding: const EdgeInsets.all(8), child: Column( children: [ Text( album.title, style: const TextStyle( color: AppColors.onSurface, fontWeight: FontWeight.w500, fontSize: 12, ), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), if (album.artist != null) Text( album.artist!.name, style: const TextStyle( color: AppColors.muted, fontSize: 11, ), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), ], ), ), ], ), ), ); } Widget _buildArtistCard(Artist artist) { return GestureDetector( onTap: () => _openArtist(artist), child: Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), border: Border.all( color: AppColors.violet.withOpacity(0.3), ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: Center( child: Container( width: 80, height: 80, decoration: BoxDecoration( shape: BoxShape.circle, gradient: AppColors.primaryGradient, ), child: artist.imageUrl != null ? ClipOval( child: Image.network( artist.imageUrl!, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.person, size: 36, color: AppColors.muted, ); }, ), ) : const Icon( Icons.person, size: 36, color: AppColors.muted, ), ), ), ), Padding( padding: const EdgeInsets.all(8), child: Text( artist.name, style: const TextStyle( color: AppColors.onSurface, fontWeight: FontWeight.w500, fontSize: 12, ), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), ), ], ), ), ); } Widget _buildEmptyState({ required IconData icon, required String message, required String submessage, }) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: 48, color: AppColors.muted, ), const SizedBox(height: 12), Text( message, style: const TextStyle( fontSize: 16, color: AppColors.onSurface, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), Text( submessage, style: const TextStyle( fontSize: 12, color: AppColors.muted, ), ), ], ), ); } Widget _buildErrorState(String error) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.error_outline, size: 48, color: AppColors.error, ), const SizedBox(height: 12), const Text( 'Something went wrong', style: TextStyle( fontSize: 16, color: AppColors.error, ), ), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( error, style: const TextStyle( fontSize: 12, color: AppColors.muted, ), textAlign: TextAlign.center, ), ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: () { ref.read(libraryProvider.notifier).refresh(); }, icon: const Icon(Icons.refresh, size: 16), label: const Text('Retry'), style: ElevatedButton.styleFrom( backgroundColor: AppColors.cyan, foregroundColor: AppColors.primary, ), ), ], ), ); } void _openPlaylist(dynamic playlist) { // TODO: Navigate to playlist details print('Opening playlist: ${playlist.name}'); } void _confirmDeletePlaylist(dynamic playlist) { showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: AppColors.surfaceVariant, title: const Text( 'Delete Playlist?', style: TextStyle(color: AppColors.onSurface), ), content: Text( 'Are you sure you want to delete "${playlist.name}"? This action cannot be undone.', style: const TextStyle(color: AppColors.muted), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text( 'Cancel', style: TextStyle(color: AppColors.muted), ), ), TextButton( onPressed: () { Navigator.pop(context); ref.read(libraryProvider.notifier).deletePlaylist(playlist.id); }, child: const Text( 'Delete', style: TextStyle(color: AppColors.error), ), ), ], ), ); } void _openAlbum(Album album) { // TODO: Navigate to album details print('Opening album: ${album.title}'); } void _openArtist(Artist artist) { // TODO: Navigate to artist details print('Opening artist: ${artist.name}'); } }