Skip to main content

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.

Musynth offers two ways to curate song collections: Favourites (cross-platform, persisted locally) and Playlists (Android-only, backed by MediaStore). This page covers how each system works, where data lives, and how to use every touch point for adding, removing, and playing from either collection.

Favourites

How favourites are stored

Favourites are persisted as a List<String> of song IDs in SharedPreferences (via PreferencesRepository.favoriteSongList). On every app launch, FavoritesCubit.initFavorites(allSongs) decodes those IDs back into SongModel instances using an O(n+m) HashMap lookup that runs inside a background Dart Isolate, so the UI thread is never blocked even with thousands of songs.

Toggling a favourite

FavoritesService.toggle is the single entry point for adding or removing a song from favourites:
void toggle(
  SongModel song, {
  required FavoritesState favoritesState,
  required List<SongModel> allSongs,
}) {
  final favoriteList = [...favoritesState.favoriteList];
  final favoriteSongList = [...favoritesState.favoriteSongList];
  final isFavorite = favoritesState.isFavoriteSong(song.id);

  if (isFavorite) {
    favoriteList.removeWhere((s) => s.id == song.id);
    favoriteSongList.removeWhere((id) => id == song.id.toString());
  } else {
    final index = allSongs.indexWhere((s) => s.id == song.id);
    if (index != -1) favoriteList.add(allSongs[index]);
    favoriteSongList.add(song.id.toString());
  }

  _cubit.updateFavorites(
    favoriteList: favoriteList,
    favoriteSongList: favoriteSongList,
  );
  _prefs.favoriteSongList = favoriteSongList; // persists to SharedPreferences
}
The method takes a copy of the existing lists (immutability), applies the mutation, emits a new FavoritesState via the cubit, and writes the updated ID list to SharedPreferences — all synchronously.

Where you can toggle a favourite

LocationHow to toggle
Now Playing screenThe heart icon (♥) next to the song title calls FavoritesService.toggle directly on press.
Song context menuThe MoreSongOptionsModal header row contains a favourite toggle button, available from any list via long-press or the ⋮ icon.
Media notificationThe persistent notification exposes a custom favorite action that toggles the currently-playing song while the app is in the background.

Favourites tab

The Favourites tab (FavoriteScreen) renders the full FavoritesCubit.state.favoriteList. Tapping a song plays the entire favourites collection as PlaylistType.favorites:
setupLocator<MusicOrchestratorService>().playSong(
  song,
  PlaylistType.favorites,
  heroId: heroId,
);
The disabledDeleteButton: true flag is set on the MoreSongOptionsModal when opened from the Favourites tab, so the Delete from device option is hidden (unfavouriting a song does not delete the file).

Playlists (Android only)

The Playlists tab and all playlist mutation operations are guarded by Platform.isAndroid. The feature is not available on iOS because it relies on the Android MediaStore content provider via the music_query_selector plugin.

Creating a playlist

1

Switch to the Playlist tab

The FAB icon changes from a shuffle arrow to a +.
2

Tap the + FAB

A CreatePlaylistDialog appears asking for a playlist name.
3

Confirm the name

The app calls MusicQuerySelector.createPlaylist(name) and then LibraryCubit.refreshPlaylist() to reload the list from MediaStore.
final onAudioQuery = setupLocator<MusicQuerySelector>();
await onAudioQuery.createPlaylist(playlistName);
libraryCubit.refreshPlaylist();

Adding a song to a playlist

  1. Long-press any song (or tap the ⋮ icon) to open MoreSongOptionsModal.
  2. Tap Add to playlist.
  3. Choose the target playlist from the dropdown.
  4. Tap Add — the app calls MusicQuerySelector.addToPlaylist(playlistId, song.id) and then LibraryCubit.refreshPlaylist().
onAudioQuery.addToPlaylist(_selectedPlaylistId!, widget.song.id);
context.read<LibraryCubit>().refreshPlaylist();

Removing a song from a playlist

From MoreSongOptionsModal, tap Remove from playlist. Because MediaStore playlist membership rows use their own _ID (not the audio file’s ID), the modal first queries queryAudiosFrom(PLAYLIST, id) and matches by file path before calling removeFromPlaylist:
final playlistSongs = await onAudioQuery.queryAudiosFrom(
    AudiosFromType.PLAYLIST, selectedPlaylistId!);
final matching = playlistSongs.where((s) => s.data == widget.song.data);
await onAudioQuery.removeFromPlaylist(
    selectedPlaylistId!, matching.first.id);
context.read<LibraryCubit>()
  ..searchByPlaylistId(selectedPlaylistId!, force: true)
  ..refreshPlaylist();
When inside a playlist’s own screen (isPlaylist: true), the Remove from this playlist option removes the song from the currently-viewed playlist directly without requiring a dropdown.

Deleting a playlist

Tap the delete (🗑) icon on a playlist row, or long-press the row. The app calls MusicQuerySelector.removePlaylist(id) and then LibraryCubit.refreshPlaylist(). A snackbar confirms success or reports failure.

Lazy playlist song loading

Playlist song lists are loaded on first visit and cached in LibraryState.playlistCollection. LibraryCubit.searchByPlaylistId(id) is called when a PlaylistSelectedScreen opens:
Future<void> searchByPlaylistId(int playlistId, {bool force = false}) async {
  if (state.playlistCollection.containsKey(playlistId) && !force) return;

  emit(state.copyWith(isLoading: true));
  final songs = await _audio.queryAudiosByPlaylistId(playlistId);
  final updated = Map<int, List<SongModel>>.from(state.playlistCollection)
    ..[playlistId] = songs;
  emit(state.copyWith(playlistCollection: updated, isLoading: false));
}
Pass force: true after any mutation (add/remove song) to bypass the cache.

Comparison

FeatureFavouritesPlaylists
PlatformiOS + AndroidAndroid only
StorageSharedPreferences (IDs as strings)Android MediaStore
Created byToggling the ♥ buttonFAB on Playlist tab
PlaylistTypePlaylistType.favoritesPlaylistType.playlist
Persists across reinstallsNo (SharedPreferences cleared)Yes (MediaStore survives reinstall)
Song orderInsertion orderMediaStore order

Build docs developers (and LLMs) love