Skip to content

feat: live search — update results as you type#75

Open
mthwJsmith wants to merge 2 commits intodddevid:masterfrom
mthwJsmith:feat/live-search
Open

feat: live search — update results as you type#75
mthwJsmith wants to merge 2 commits intodddevid:masterfrom
mthwJsmith:feat/live-search

Conversation

@mthwJsmith
Copy link

@mthwJsmith mthwJsmith commented Feb 27, 2026

Summary

Personally i dislike the popup live search on the search page, and find it superior and matching other popular music apps just to show the live search in the already excellent search page.

I found overlay to be quite a cramped floating card that filled up with unrelated albums for most of my searches (I do use squid.wtf backend which made this feel worse). The full results view imo has much better presentation (I really like it of all the Navidrome clients I used) - horizontal album cards, proper SongTile rows with controls, ArtistTile rows. Both call the exact same API, so there's no reason not to show the better view live as you type.

image
screen-20260227-030402-1772161431980.mp4

In my opinion, I would just remove the popup, but for this PR I added an options in settings to keep the popup card.

Changes

File Change
player_ui_settings_service.dart New _keyLiveSearch setting + getter/setter + ValueNotifier
search_screen.dart Read setting in initState, branch _onSearchChanged to call _search() or _loadAutocomplete()
settings_display_tab.dart New "Search" section with Live Search toggle (CupertinoSwitch)
app_en.arb 3 new English strings (Crowdin handles other languages)

Test plan

  • Open search, type a query → results update live in full view (no overlay)
  • Settings → Display → toggle Live Search OFF → overlay appears as before
  • Toggle back ON → live search resumes
  • Press Enter while typing → full results still render (no regression)
  • Clear search field → browse categories shown (no regression)

Summary by CodeRabbit

  • New Features
    • Added live search functionality that updates search results in real-time as you type, providing instant feedback instead of a traditional dropdown menu.
    • Added a configurable live search option in settings to toggle between live search and dropdown results, enabled by default.

Adds a "Live Search" setting in Display Settings (default: ON).
When enabled, typing in the search box updates the full results
view in real time (debounced 300ms) instead of showing the
autocomplete dropdown overlay. The overlay behavior is preserved
when the setting is turned off.
@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0330fd4 and 606ea90.

📒 Files selected for processing (1)
  • lib/screens/search_screen.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/screens/search_screen.dart

📝 Walkthrough

Walkthrough

Adds a live search feature: three new localization keys, a persistent toggle in settings, a PlayerUiSettingsService flag/notifier and persistence, and search screen changes to conditionally perform live searches versus autocomplete based on the setting.

Changes

Cohort / File(s) Summary
Localization Resources
lib/l10n/app_en.arb
Added liveSearchSection, liveSearch, and liveSearchSubtitle localization keys for the new UI text.
Settings Persistence Service
lib/services/player_ui_settings_service.dart
Added _keyLiveSearch, liveSearchNotifier, setLiveSearch(), and getLiveSearch(); initialization now populates liveSearchNotifier from SharedPreferences (default true).
Settings UI Display
lib/screens/settings_display_tab.dart
Added _liveSearch state, new liveSearchSection UI, and _buildLiveSearchToggle() with a CupertinoSwitch that calls setLiveSearch() to persist changes.
Search Screen Logic
lib/screens/search_screen.dart
Added _liveSearch state, subscribe/unsubscribe to liveSearchNotifier, initialize in initState(), and branch debounce handler to run full search when live search is enabled or load autocomplete when disabled.

Sequence Diagram

sequenceDiagram
    actor User
    participant SettingsUI as Settings Display UI
    participant Service as PlayerUiSettingsService
    participant Storage as SharedPreferences
    participant SearchScreen as Search Screen

    User->>SettingsUI: Toggle Live Search switch
    SettingsUI->>Service: call setLiveSearch(value)
    Service->>Storage: persist "search_live_search" = value
    Service->>Service: liveSearchNotifier.value = value
    SearchScreen->>Service: listen/reads liveSearchNotifier
    SearchScreen->>SearchScreen: on input debounce -> if liveSearch true run full search else load autocomplete
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I nibble keys and watch results fly,

live search that answers as you try.
Flip the switch, the results appear,
no dropdown waits — just instant cheer. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main feature addition: live search functionality that updates results as the user types, matching the primary objective of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/screens/search_screen.dart (1)

41-45: Setting is not reactive to changes made while screen is mounted.

The _liveSearch value is read once in initState(). If a user toggles the setting in the Display tab and returns to this screen without triggering a rebuild, the search behavior won't reflect the change until the widget is recreated.

Consider listening to PlayerUiSettingsService().liveSearchNotifier for reactive updates:

♻️ Suggested improvement for reactive setting updates
 class _SearchScreenState extends State<SearchScreen> {
   final _searchController = TextEditingController();
   final _focusNode = FocusNode();
   SearchResult? _searchResult;
   SearchResult? _autocompleteSuggestions;
   bool _isSearching = false;
   bool _isLoadingSuggestions = false;
   bool _showSuggestions = false;
   String _query = '';
   Timer? _debounceTimer;
   bool _liveSearch = true;
+  final _uiSettings = PlayerUiSettingsService();

   `@override`
   void initState() {
     super.initState();
-    _liveSearch = PlayerUiSettingsService().getLiveSearch();
+    _liveSearch = _uiSettings.getLiveSearch();
+    _uiSettings.liveSearchNotifier.addListener(_onLiveSearchChanged);
+  }
+
+  void _onLiveSearchChanged() {
+    setState(() {
+      _liveSearch = _uiSettings.liveSearchNotifier.value;
+    });
   }

   `@override`
   void dispose() {
     _searchController.dispose();
     _focusNode.dispose();
     _debounceTimer?.cancel();
+    _uiSettings.liveSearchNotifier.removeListener(_onLiveSearchChanged);
     super.dispose();
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/screens/search_screen.dart` around lines 41 - 45, The screen reads
PlayerUiSettingsService().getLiveSearch() once in initState(), so _liveSearch
won't update when the user changes the setting; subscribe to
PlayerUiSettingsService().liveSearchNotifier in initState (store the returned
Listenable/ValueNotifier subscription if needed), update _liveSearch inside a
setState callback when the notifier fires, and remove the listener in dispose to
avoid leaks; keep the initial getLiveSearch() for the initial value but rely on
the notifier for reactive updates (refer to _liveSearch, initState, dispose, and
PlayerUiSettingsService().liveSearchNotifier).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/screens/search_screen.dart`:
- Around line 41-45: The screen reads PlayerUiSettingsService().getLiveSearch()
once in initState(), so _liveSearch won't update when the user changes the
setting; subscribe to PlayerUiSettingsService().liveSearchNotifier in initState
(store the returned Listenable/ValueNotifier subscription if needed), update
_liveSearch inside a setState callback when the notifier fires, and remove the
listener in dispose to avoid leaks; keep the initial getLiveSearch() for the
initial value but rely on the notifier for reactive updates (refer to
_liveSearch, initState, dispose, and
PlayerUiSettingsService().liveSearchNotifier).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2233ba and 0330fd4.

📒 Files selected for processing (4)
  • lib/l10n/app_en.arb
  • lib/screens/search_screen.dart
  • lib/screens/settings_display_tab.dart
  • lib/services/player_ui_settings_service.dart

The setting was only read once in initState(). Now listens to
the ValueNotifier so toggling Live Search in Settings takes
effect immediately without leaving the search screen.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant