import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../services/music_queue_service.dart'; import '../models/spotify_track.dart'; class NowPlayingPage extends StatelessWidget { const NowPlayingPage({super.key}); @override Widget build(BuildContext context) { return Consumer( builder: (context, queueService, child) { final currentTrack = queueService.currentTrack; final queue = queueService.queue; return Scaffold( backgroundColor: const Color(0xFF121212), body: SafeArea( child: SingleChildScrollView( child: Column( children: [ // Now Playing Header Container( padding: const EdgeInsets.all(20), child: const Text( 'Now Playing', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), // Current Track Display Container( height: MediaQuery.of(context).size.height * 0.5, margin: const EdgeInsets.all(20), child: currentTrack != null ? _buildCurrentTrackCard(context, currentTrack, queueService) : _buildNoTrackCard(), ), // Playback Controls Container( padding: const EdgeInsets.all(20), child: _buildPlaybackControls(queueService), ), // Queue Preview Container( height: MediaQuery.of(context).size.height * 0.3, margin: const EdgeInsets.symmetric(horizontal: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(bottom: 10), child: Text( 'Up Next', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), Expanded( child: queue.isEmpty ? const Center( child: Text( 'No songs in queue\nGo to Voting to add some!', textAlign: TextAlign.center, style: TextStyle( color: Colors.grey, fontSize: 16, ), ), ) : ListView.builder( itemCount: queue.length, itemBuilder: (context, index) { return _buildQueueItem(queue[index], index + 1); }, ), ), ], ), ), const SizedBox(height: 20), // Extra padding at bottom ], ), ), ), ); }, ); } Widget _buildCurrentTrackCard(BuildContext context, SpotifyTrack currentTrack, MusicQueueService queueService) { return Card( color: const Color(0xFF1E1E1E), child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Album Art Container( width: 160, height: 160, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: const Color(0xFF2A2A2A), ), child: currentTrack.album.images.isNotEmpty ? ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.network( currentTrack.album.images.first.url, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.music_note, size: 80, color: Colors.grey, ); }, ), ) : const Icon( Icons.music_note, size: 80, color: Colors.grey, ), ), const SizedBox(height: 15), // Track Info Text( currentTrack.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white, ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Text( currentTrack.artists.map((a) => a.name).join(', '), style: const TextStyle( fontSize: 14, color: Colors.grey, ), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Text( currentTrack.album.name, style: const TextStyle( fontSize: 12, color: Colors.grey, ), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, ), // Progress Bar const SizedBox(height: 15), Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Column( children: [ LinearProgressIndicator( value: queueService.progress, backgroundColor: Colors.grey[800], valueColor: const AlwaysStoppedAnimation(Color(0xFF6366F1)), ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( _formatDuration((queueService.progress * currentTrack.durationMs / 1000).round()), style: const TextStyle(color: Colors.grey, fontSize: 12), ), Text( currentTrack.duration, style: const TextStyle(color: Colors.grey, fontSize: 12), ), ], ), ], ), ), ], ), ), ); } Widget _buildNoTrackCard() { return Card( color: const Color(0xFF1E1E1E), child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.music_off, size: 80, color: Colors.grey, ), SizedBox(height: 20), Text( 'No track playing', style: TextStyle( fontSize: 20, color: Colors.grey, ), ), SizedBox(height: 10), Text( 'Add some songs from the Voting tab!', style: TextStyle( fontSize: 16, color: Colors.grey, ), ), ], ), ), ); } Widget _buildPlaybackControls(MusicQueueService queueService) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // Previous (disabled for now) IconButton( onPressed: null, icon: const Icon(Icons.skip_previous), iconSize: 40, color: Colors.grey, ), // Play/Pause IconButton( onPressed: queueService.togglePlayPause, icon: Icon(queueService.isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled), iconSize: 60, color: const Color(0xFF6366F1), ), // Next IconButton( onPressed: queueService.queue.isNotEmpty ? queueService.skipTrack : null, icon: const Icon(Icons.skip_next), iconSize: 40, color: queueService.queue.isNotEmpty ? Colors.white : Colors.grey, ), ], ); } Widget _buildQueueItem(QueueItem item, int position) { return Card( color: const Color(0xFF1E1E1E), margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: CircleAvatar( backgroundColor: const Color(0xFF6366F1), child: Text( '$position', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ), ), title: Text( item.track.name, style: const TextStyle(color: Colors.white, fontSize: 14), maxLines: 1, overflow: TextOverflow.ellipsis, ), subtitle: Text( item.track.artists.map((a) => a.name).join(', '), style: const TextStyle(color: Colors.grey, fontSize: 12), maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.thumb_up, color: Colors.green, size: 16), const SizedBox(width: 4), Text( '${item.votes}', style: const TextStyle(color: Colors.green, fontSize: 12), ), ], ), ), ); } String _formatDuration(int seconds) { int minutes = seconds ~/ 60; int remainingSeconds = seconds % 60; return '${minutes}:${remainingSeconds.toString().padLeft(2, '0')}'; } }