Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
22 changes: 21 additions & 1 deletion lib/screens/search_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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});
Expand All @@ -35,9 +36,24 @@ class _SearchScreenState extends State<SearchScreen> {
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();
Expand Down Expand Up @@ -93,7 +109,11 @@ class _SearchScreenState extends State<SearchScreen> {
}

_debounceTimer = Timer(const Duration(milliseconds: 300), () {
_loadAutocomplete(value);
if (_liveSearch) {
_search(value);
} else {
_loadAutocomplete(value);
}
});
}

Expand Down
51 changes: 51 additions & 0 deletions lib/screens/settings_display_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class _SettingsDisplayTabState extends State<SettingsDisplayTab> {
String _artworkShape = 'rounded';
String _artworkShadow = 'soft';
String _artworkShadowColor = 'black';
bool _liveSearch = true;

bool get _isDesktop {
if (kIsWeb) return false;
Expand All @@ -50,6 +51,7 @@ class _SettingsDisplayTabState extends State<SettingsDisplayTab> {
_artworkShape = _playerUiSettings.getArtworkShape();
_artworkShadow = _playerUiSettings.getArtworkShadow();
_artworkShadowColor = _playerUiSettings.getArtworkShadowColor();
_liveSearch = _playerUiSettings.getLiveSearch();
});
}

Expand Down Expand Up @@ -77,6 +79,13 @@ class _SettingsDisplayTabState extends State<SettingsDisplayTab> {
],
),
const SizedBox(height: 24),
_buildSection(
title: AppLocalizations.of(context)!.liveSearchSection.toUpperCase(),
children: [
_buildLiveSearchToggle(),
],
),
const SizedBox(height: 24),
_buildSection(
title: AppLocalizations.of(
context,
Expand Down Expand Up @@ -231,6 +240,48 @@ class _SettingsDisplayTabState extends State<SettingsDisplayTab> {
);
}

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() {
Expand Down
13 changes: 13 additions & 0 deletions lib/services/player_ui_settings_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -17,6 +18,7 @@ class PlayerUiSettingsService {
SharedPreferences? _prefs;

final ValueNotifier<bool> showStarRatingsNotifier = ValueNotifier(false);
final ValueNotifier<bool> liveSearchNotifier = ValueNotifier(true);
final ValueNotifier<double> albumArtCornerRadiusNotifier = ValueNotifier(8.0);

/// 'rounded' | 'circle' | 'square'
Expand All @@ -37,6 +39,7 @@ class PlayerUiSettingsService {
artworkShapeNotifier.value = getArtworkShape();
artworkShadowNotifier.value = getArtworkShadow();
artworkShadowColorNotifier.value = getArtworkShadowColor();
liveSearchNotifier.value = getLiveSearch();
}

Future<void> setShowVolumeSlider(bool show) async {
Expand Down Expand Up @@ -97,4 +100,14 @@ class PlayerUiSettingsService {
String getArtworkShadowColor() {
return _prefs?.getString(_keyArtworkShadowColor) ?? 'black';
}

Future<void> setLiveSearch(bool enabled) async {
await initialize();
await _prefs!.setBool(_keyLiveSearch, enabled);
liveSearchNotifier.value = enabled;
}

bool getLiveSearch() {
return _prefs?.getBool(_keyLiveSearch) ?? true;
}
}