Musynth includes a full-screen search experience (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/CesarArellano/Music-Player-App/llms.txt
Use this file to discover all available pages before exploring further.
MusicSearchScreen) that filters your entire local library — songs, albums, and artists — as you type. Results are computed off the main thread with a 150 ms debounce, and a microphone button lets you dictate queries via the speech_to_text package. This page explains the UI, the search algorithm, voice input, and how results are displayed.
Opening search
Tap the magnifying-glass icon (🔍) in the app bar of the home screen, or from anyAlbumSelectedScreen or ArtistSelectedScreen — all three push AppRoutes.search via context.pushNamed(AppRoutes.search).
MusicSearchScreen is a custom StatefulWidget, not a SearchDelegate subclass. Its scaffold is transparent so the global AppBackground shows through, matching the home screen’s visual style.
Search field
The text field is a rounded pill shape with:- A search icon (
Icons.search) as a prefix. - A × clear button (
Icons.close) as a suffix when the field has text. - A mic button (
Icons.mic) as a suffix when the field is empty — turns red while listening. - An external Cancel button to the right of the pill that pops the screen.
Text search
Debounce
Every keystroke cancels the previousTimer and schedules a new one for 150 ms. An empty field immediately clears the results with no Isolate overhead:
Off-thread filtering
When the timer fires,_runSearch delegates to LibraryState.searchAsync, which executes the filter logic inside Isolate.run():
.toLowerCase().contains(query.toLowerCase()) on each item — and it runs entirely off the main thread so scrolling the results list stays smooth.
Stale-result guard
If the user keeps typing while an Isolate is running, the result is silently discarded when it arrives (query != _query). Only the freshest query’s result is ever displayed.
Voice search
Thespeech_to_text package powers voice input. The mic button calls _toggleListening():
TextEditingController, which in turn fires the normal debounce → search pipeline:
Voice search initialises asynchronously when the screen opens (
_initSpeech()). If SpeechToText.initialize() returns false (microphone permission denied or speech services unavailable), the mic button is silently disabled — no error is surfaced to the user.Search results
Results arrive as aMultipleSearchModel containing three lists:
| Field | Type | Contents |
|---|---|---|
songs | List<SongModel> | Tracks whose title or artist contains the query |
albums | List<AlbumModel> | Albums whose name contains the query |
artists | List<ArtistModel> | Artists whose name contains the query |
- Songs
- Artists
- Albums
Each song tile shows artwork, title, and artist. Tapping plays the song immediately as
PlaylistType.songs and pushes the Now Playing screen. Long-pressing opens MoreSongOptionsModal.Performance summary
Why is search fast even with large libraries?
Why is search fast even with large libraries?
Off-thread execution:
LibraryState.searchAsync passes the full song, album, and artist lists into Isolate.run(). The filter loop never touches the main thread, so the UI remains responsive.150 ms debounce: Only one Isolate is spawned per “pause” in typing, not per keystroke. For fast typists this can reduce Isolate launches by 5–10× compared to no debounce.Stale-result discard: Overlapping queries never race to update the UI. The screen always shows the result for the most recent query.In-memory data: All three collections (songList, albumList, artistList) are already in LibraryState from the initial scan — no I/O happens during search.