import 'package:flutter/material.dart'; import '../services/spotify_service.dart'; import '../models/spotify_track.dart'; class VotingPage extends StatefulWidget { const VotingPage({super.key}); @override State createState() => _VotingPageState(); } class _VotingPageState extends State { final TextEditingController _searchController = TextEditingController(); final SpotifyService _spotifyService = SpotifyService(); List> suggestedSongs = [ { "id": "1", "title": "Ocean Breeze", "artist": "Lofi Dreams", "votes": 12, "userVoted": false, "duration": "3:45", "imageUrl": "https://i.scdn.co/image/ocean" }, { "id": "2", "title": "Sunset Melody", "artist": "Acoustic Soul", "votes": 8, "userVoted": true, "duration": "4:12", "imageUrl": "https://i.scdn.co/image/sunset" }, { "id": "3", "title": "Peaceful Waters", "artist": "Nature Sounds", "votes": 5, "userVoted": false, "duration": "3:20", "imageUrl": "https://i.scdn.co/image/water" }, { "id": "4", "title": "Summer Nights", "artist": "Chill Vibes", "votes": 3, "userVoted": false, "duration": "3:55", "imageUrl": "https://i.scdn.co/image/night" }, ]; List searchResults = []; bool isSearching = false; Future _searchSpotify(String query) async { if (query.isEmpty) { setState(() { searchResults = []; isSearching = false; }); return; } setState(() { isSearching = true; }); try { final tracks = await _spotifyService.searchTracks(query, limit: 10); setState(() { searchResults = tracks; isSearching = false; }); } catch (e) { print('Error searching Spotify: $e'); setState(() { searchResults = []; isSearching = false; }); } } void _upvote(int index) { setState(() { suggestedSongs[index]["votes"]++; if (!suggestedSongs[index]["userVoted"]) { suggestedSongs[index]["userVoted"] = true; } // Sort by votes to update queue order suggestedSongs.sort((a, b) => b["votes"].compareTo(a["votes"])); }); } void _downvote(int index) { setState(() { if (suggestedSongs[index]["votes"] > 0) { suggestedSongs[index]["votes"]--; } // Sort by votes to update queue order suggestedSongs.sort((a, b) => b["votes"].compareTo(a["votes"])); }); } void _addToQueue(SpotifyTrack track) { setState(() { suggestedSongs.insert(0, { "id": track.id, "title": track.name, "artist": track.artistNames, "votes": 1, "userVoted": true, "duration": track.duration, "imageUrl": track.imageUrl, }); }); // Show confirmation ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Added "${track.name}" to queue!'), backgroundColor: const Color(0xFF6366F1), duration: const Duration(seconds: 2), ), ); // Clear search _searchController.clear(); setState(() { searchResults = []; }); } @override Widget build(BuildContext context) { return Container( color: const Color(0xFF121212), child: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Search Section const Text( 'Suggest a Song', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white, ), ), const SizedBox(height: 15), // Search Bar Container( decoration: BoxDecoration( color: const Color(0xFF1E1E1E), borderRadius: BorderRadius.circular(12), ), child: TextField( controller: _searchController, style: const TextStyle(color: Colors.white), decoration: const InputDecoration( hintText: 'Search for songs, artists...', hintStyle: TextStyle(color: Colors.grey), prefixIcon: Icon(Icons.search, color: Color(0xFF6366F1)), border: InputBorder.none, contentPadding: EdgeInsets.all(16), ), onChanged: (value) { // Search Spotify when user types _searchSpotify(value); }, ), ), // Search Results (shown when typing) if (_searchController.text.isNotEmpty || searchResults.isNotEmpty) ...[ const SizedBox(height: 15), Row( children: [ const Text( 'Search Results', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white, ), ), if (isSearching) ...[ const SizedBox(width: 10), const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Color(0xFF6366F1)), ), ), ], ], ), const SizedBox(height: 10), Container( height: 200, child: searchResults.isEmpty && !isSearching ? const Center( child: Text( 'No tracks found', style: TextStyle(color: Colors.grey), ), ) : ListView.builder( itemCount: searchResults.length, itemBuilder: (context, index) { final track = searchResults[index]; return Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF1E1E1E), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: const Color(0xFF6366F1).withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: track.imageUrl.isNotEmpty ? ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( track.imageUrl, width: 40, height: 40, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.music_note, color: Color(0xFF6366F1), size: 20, ); }, ), ) : const Icon( Icons.music_note, color: Color(0xFF6366F1), size: 20, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( track.name, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w500, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( "${track.artistNames} • ${track.duration}", style: const TextStyle( color: Colors.grey, fontSize: 12, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ElevatedButton( onPressed: () => _addToQueue(track), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF6366F1), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), minimumSize: const Size(60, 30), ), child: const Text( 'Add', style: TextStyle(fontSize: 12), ), ), ], ), ); }, ), ), ], const SizedBox(height: 20), // Voting Queue Section Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Community Queue', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color(0xFF6366F1).withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: const Text( '↑↓ Vote to reorder', style: TextStyle( color: Color(0xFF6366F1), fontSize: 11, fontWeight: FontWeight.w500, ), ), ), ], ), const SizedBox(height: 15), // Queue List Expanded( child: ListView.builder( itemCount: suggestedSongs.length, itemBuilder: (context, index) { final song = suggestedSongs[index]; return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: const Color(0xFF1E1E1E), borderRadius: BorderRadius.circular(12), border: song["userVoted"] ? Border.all(color: const Color(0xFF6366F1), width: 1) : null, ), child: Row( children: [ // Queue Position Container( width: 30, height: 30, decoration: BoxDecoration( color: index < 3 ? const Color(0xFF6366F1) : const Color(0xFF6366F1).withOpacity(0.3), shape: BoxShape.circle, ), child: Center( child: Text( '${index + 1}', style: TextStyle( color: index < 3 ? Colors.white : Colors.grey, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), ), const SizedBox(width: 12), // Album Art Container( width: 50, height: 50, decoration: BoxDecoration( color: const Color(0xFF6366F1).withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: song["imageUrl"] != null && song["imageUrl"].isNotEmpty ? ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( song["imageUrl"], width: 50, height: 50, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.music_note, color: Color(0xFF6366F1), size: 24, ); }, ), ) : const Icon( Icons.music_note, color: Color(0xFF6366F1), size: 24, ), ), const SizedBox(width: 15), // Song Info Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( song["title"], style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, fontSize: 16, ), ), const SizedBox(height: 4), Text( "${song["artist"]} • ${song["duration"]}", style: const TextStyle( color: Colors.grey, fontSize: 14, ), ), ], ), ), // Vote Buttons Column( children: [ // Upvote Button GestureDetector( onTap: () => _upvote(index), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFF22C55E).withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.keyboard_arrow_up, color: Color(0xFF22C55E), size: 20, ), ), ), const SizedBox(height: 8), // Vote Count Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color(0xFF6366F1).withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Text( '${song["votes"]}', style: const TextStyle( color: Color(0xFF6366F1), fontWeight: FontWeight.bold, fontSize: 14, ), ), ), const SizedBox(height: 8), // Downvote Button GestureDetector( onTap: () => _downvote(index), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFFEF4444).withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.keyboard_arrow_down, color: Color(0xFFEF4444), size: 20, ), ), ), ], ), ], ), ); }, ), ), ], ), ), ); } @override void dispose() { _searchController.dispose(); super.dispose(); } }