/// Search Page - Mobile Layout library; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/theme/colors.dart'; import '../../providers/search_provider.dart'; import '../../providers/music_provider.dart'; import '../../../domain/entities/track.dart'; import '../../../domain/entities/artist.dart'; import '../../../domain/entities/album.dart'; import '../../widgets/search/search_track_card.dart'; import '../../widgets/search/search_artist_card.dart'; import '../../widgets/search/search_album_card.dart'; class SearchMobilePage extends ConsumerStatefulWidget { const SearchMobilePage({super.key}); @override ConsumerState createState() => _SearchMobilePageState(); } class _SearchMobilePageState extends ConsumerState { final _controller = TextEditingController(); final _focusNode = FocusNode(); @override void dispose() { _controller.dispose(); _focusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Column( children: [ // Search bar Padding( padding: const EdgeInsets.all(16), child: _buildSearchBar(), ), // Results (same as desktop but different layout) Expanded( child: _buildResults(), ), ], ); } Widget _buildSearchBar() { // Similar to desktop but smaller return Container( decoration: BoxDecoration( color: AppColors.surfaceVariant, borderRadius: BorderRadius.circular(24), border: Border.all( color: AppColors.cyan.withOpacity(0.2), width: 2, ), ), child: TextField( controller: _controller, focusNode: _focusNode, onChanged: (value) { ref.read(searchProvider.notifier).search(value); setState(() {}); }, decoration: const InputDecoration( hintText: 'Search...', hintStyle: TextStyle(color: AppColors.muted), prefixIcon: Icon(Icons.search, color: AppColors.cyan), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 14), ), ), ); } Widget _buildResults() { // Reuse desktop logic but with 2-column grid final searchState = ref.watch(searchProvider); if (searchState.query.isEmpty) { return _buildEmptyState(); } if (searchState.isSearching) { return const Center( child: CircularProgressIndicator(color: AppColors.cyan), ); } if (searchState.error != null) { return _buildErrorState(searchState.error ?? 'Unknown error'); } if (searchState.totalResults == 0) { return _buildNoResultsState(); } return CustomScrollView( slivers: [ // Tracks section if (searchState.tracks.isNotEmpty) SliverToBoxAdapter( child: _buildSection('Tracks', searchState.tracks), ), // Artists section if (searchState.artists.isNotEmpty) SliverToBoxAdapter( child: _buildSection('Artists', searchState.artists), ), // Albums section if (searchState.albums.isNotEmpty) SliverToBoxAdapter( child: _buildSection('Albums', searchState.albums), ), ], ); } Widget _buildSection(String title, List items) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), child: Text( title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: AppColors.cyan, ), ), ), GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsets.symmetric(horizontal: 16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1, ), itemCount: items.length, itemBuilder: (context, index) { return _buildResultCard(items[index]); }, ), ], ); } Widget _buildResultCard(dynamic item) { final playerNotifier = ref.read(playerProvider.notifier); if (item is Track) { // It's a track - play on tap return SearchTrackCard( track: item, onTap: () => _playTrack(item, playerNotifier), ); } else if (item is Artist) { // It's an artist - show details (TODO: navigate) return SearchArtistCard( artist: item, onTap: () => _showArtistDetails(item), ); } else if (item is Album) { // It's an album - show details (TODO: navigate) return SearchAlbumCard( album: item, onTap: () => _showAlbumDetails(item), ); } return const SizedBox.shrink(); } void _playTrack(Track track, PlayerNotifier playerNotifier) { // Set as queue and play playerNotifier.setQueue([track], startIndex: 0); playerNotifier.loadTrack(track); playerNotifier.play(); } void _showArtistDetails(Artist artist) { // TODO: Navigate to artist details page print('Show artist: ${artist.name}'); } void _showAlbumDetails(Album album) { // TODO: Navigate to album details page print('Show album: ${album.title}'); } Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.search, size: 48, color: AppColors.muted, ), const SizedBox(height: 12), const Text( 'Search for your favorite music', style: TextStyle( fontSize: 16, color: AppColors.muted, ), ), ], ), ); } Widget _buildErrorState(String error) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ 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), Text( error, style: const TextStyle( fontSize: 12, color: AppColors.muted, ), ), ], ), ); } Widget _buildNoResultsState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.music_off, size: 48, color: AppColors.muted, ), const SizedBox(height: 12), const Text( 'No results found', style: TextStyle( fontSize: 16, color: AppColors.muted, ), ), ], ), ); } }