diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0c75028..24480cb 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -693,6 +693,9 @@ "tryDifferentSearch": "Try a different search", "noSuggestions": "No suggestions", "browseCategories": "Browse Categories", + "liveSearchSection": "Search", + "liveSearch": "Live Search", + "liveSearchSubtitle": "Update results as you type instead of showing a dropdown", "categoryMadeForYou": "Made For You", "categoryNewReleases": "New Releases", "categoryTopRated": "Top Rated", diff --git a/lib/screens/search_screen.dart b/lib/screens/search_screen.dart index 35b686b..a049895 100644 --- a/lib/screens/search_screen.dart +++ b/lib/screens/search_screen.dart @@ -17,6 +17,7 @@ import 'top_rated_screen.dart'; import 'favorites_screen.dart'; import 'radio_screen.dart'; import '../l10n/app_localizations.dart'; +import '../services/player_ui_settings_service.dart'; class SearchScreen extends StatefulWidget { const SearchScreen({super.key}); @@ -35,9 +36,24 @@ class _SearchScreenState extends State { bool _showSuggestions = false; String _query = ''; Timer? _debounceTimer; + bool _liveSearch = true; + + @override + void initState() { + super.initState(); + _liveSearch = PlayerUiSettingsService().getLiveSearch(); + PlayerUiSettingsService().liveSearchNotifier.addListener(_onLiveSearchChanged); + } + + void _onLiveSearchChanged() { + setState(() { + _liveSearch = PlayerUiSettingsService().liveSearchNotifier.value; + }); + } @override void dispose() { + PlayerUiSettingsService().liveSearchNotifier.removeListener(_onLiveSearchChanged); _searchController.dispose(); _focusNode.dispose(); _debounceTimer?.cancel(); @@ -93,7 +109,11 @@ class _SearchScreenState extends State { } _debounceTimer = Timer(const Duration(milliseconds: 300), () { - _loadAutocomplete(value); + if (_liveSearch) { + _search(value); + } else { + _loadAutocomplete(value); + } }); } diff --git a/lib/screens/settings_display_tab.dart b/lib/screens/settings_display_tab.dart index 1ba41dc..f006c19 100644 --- a/lib/screens/settings_display_tab.dart +++ b/lib/screens/settings_display_tab.dart @@ -26,6 +26,7 @@ class _SettingsDisplayTabState extends State { String _artworkShape = 'rounded'; String _artworkShadow = 'soft'; String _artworkShadowColor = 'black'; + bool _liveSearch = true; bool get _isDesktop { if (kIsWeb) return false; @@ -50,6 +51,7 @@ class _SettingsDisplayTabState extends State { _artworkShape = _playerUiSettings.getArtworkShape(); _artworkShadow = _playerUiSettings.getArtworkShadow(); _artworkShadowColor = _playerUiSettings.getArtworkShadowColor(); + _liveSearch = _playerUiSettings.getLiveSearch(); }); } @@ -77,6 +79,13 @@ class _SettingsDisplayTabState extends State { ], ), const SizedBox(height: 24), + _buildSection( + title: AppLocalizations.of(context)!.liveSearchSection.toUpperCase(), + children: [ + _buildLiveSearchToggle(), + ], + ), + const SizedBox(height: 24), _buildSection( title: AppLocalizations.of( context, @@ -231,6 +240,48 @@ class _SettingsDisplayTabState extends State { ); } + Widget _buildLiveSearchToggle() { + return ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + leading: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFFFF9500), Color(0xFFFFCC00)], + ), + borderRadius: BorderRadius.circular(8), + ), + child: const Icon( + CupertinoIcons.search, + color: Colors.white, + size: 18, + ), + ), + title: Text( + AppLocalizations.of(context)!.liveSearch, + style: const TextStyle(fontSize: 16), + ), + subtitle: Text( + AppLocalizations.of(context)!.liveSearchSubtitle, + style: TextStyle( + fontSize: 13, + color: _isDark + ? AppTheme.darkSecondaryText + : AppTheme.lightSecondaryText, + ), + ), + trailing: CupertinoSwitch( + value: _liveSearch, + activeTrackColor: AppTheme.appleMusicRed, + onChanged: (value) async { + setState(() => _liveSearch = value); + await _playerUiSettings.setLiveSearch(value); + }, + ), + ); + } + // ── Artwork Style Editor ────────────────────────────────────────────────── double _artworkPreviewRadius() { diff --git a/lib/services/player_ui_settings_service.dart b/lib/services/player_ui_settings_service.dart index 4dfa745..16abd6c 100644 --- a/lib/services/player_ui_settings_service.dart +++ b/lib/services/player_ui_settings_service.dart @@ -8,6 +8,7 @@ class PlayerUiSettingsService { static const String _keyArtworkShape = 'artwork_shape'; static const String _keyArtworkShadow = 'artwork_shadow'; static const String _keyArtworkShadowColor = 'artwork_shadow_color'; + static const String _keyLiveSearch = 'search_live_search'; static final PlayerUiSettingsService _instance = PlayerUiSettingsService._internal(); @@ -17,6 +18,7 @@ class PlayerUiSettingsService { SharedPreferences? _prefs; final ValueNotifier showStarRatingsNotifier = ValueNotifier(false); + final ValueNotifier liveSearchNotifier = ValueNotifier(true); final ValueNotifier albumArtCornerRadiusNotifier = ValueNotifier(8.0); /// 'rounded' | 'circle' | 'square' @@ -37,6 +39,7 @@ class PlayerUiSettingsService { artworkShapeNotifier.value = getArtworkShape(); artworkShadowNotifier.value = getArtworkShadow(); artworkShadowColorNotifier.value = getArtworkShadowColor(); + liveSearchNotifier.value = getLiveSearch(); } Future setShowVolumeSlider(bool show) async { @@ -97,4 +100,14 @@ class PlayerUiSettingsService { String getArtworkShadowColor() { return _prefs?.getString(_keyArtworkShadowColor) ?? 'black'; } + + Future setLiveSearch(bool enabled) async { + await initialize(); + await _prefs!.setBool(_keyLiveSearch, enabled); + liveSearchNotifier.value = enabled; + } + + bool getLiveSearch() { + return _prefs?.getBool(_keyLiveSearch) ?? true; + } }