Musynth separates the contract for audio playback from its concrete implementation through an abstract Dart interface.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.
PlaybackService declares every operation the rest of the app can perform on the audio engine; JustAudioPlaybackService fulfils that contract using the just_audio package and keeps multiple Cubits in sync via five stream subscriptions that are active for the entire lifetime of the app.
This page covers the full interface, the implementation’s construction and subscription setup, every method’s behaviour, and the _ReorderableShuffleOrder helper used to make drag-reordering work correctly in shuffle mode.
PlaybackService Interface
The player’s current effective index sequence. In shuffle mode this is the shuffled order; in normal mode it mirrors the source list order. Used by the queue screen to display songs in the order they will play.
Loads
songs into the player, sets the audio source list, and initialises playback state in all Cubits. Optionally seeks to initialPosition before playback begins (used when restoring state on app launch).Removes a single song from the live queue. If the removed song is currently playing,
just_audio advances seamlessly to the next track. Removing the last song stops playback.Moves the song at
oldIndex to newIndex in the live queue without reloading the playlist or interrupting playback.Stops the player and wipes both queue and current-song state from all Cubits, including the persisted
lastSongId. The media notification is dismissed. Used by the notification’s Close action and the queue screen’s Clear queue menu.JustAudioPlaybackService
JustAudioPlaybackService is registered as the concrete singleton for PlaybackService in di.dart. It is eagerly created in setupServiceLayer (not lazily), so the five stream subscriptions begin as soon as the service layer is set up — before the first frame renders.
Construction
_subscribe* methods.
Stream Subscriptions
_subscribeToPosition
_subscribeToPosition
AudioControlCubit with the playback position on every tick. lastSongDuration is not written here — it is persisted only on pause() and stop() to avoid constant SharedPreferences writes._subscribeToCurrentIndex
_subscribeToCurrentIndex
just_audio advances to a new track (auto-advance or skip), this listener updates the cubit song, persists lastSongId, pushes the new MediaItem to the handler, and refreshes the favourite icon in the notification._subscribeToPlaying
_subscribeToPlaying
just_audio’s playing state into PlaybackStateCubit so the UI play/pause button stays in sync._subscribeToShuffle
_subscribeToShuffle
PlaybackStateCubit.isShuffling in sync for the shuffle toggle button._subscribeToFavorites
_subscribeToFavorites
Method Implementations
loadPlaylist
- Songs with a
nulldatapath are silently filtered out — they cannot be played. - Artwork URIs point to pre-cached files:
file://<appDirectory>/<albumId>.jpg(written byArtworkCacheService). - The custom
_shuffleOrderinstance is passed tosetAudioSourcesso drag-reorder in shuffle mode works correctly (see below).
pause and stop
Both methods persist the current position to PreferencesRepository.lastSongDuration before pausing or stopping the player, so the position survives process death:
removeFromQueue
MediaItem tag ID (not by cubit index) because songs without a file path are filtered out of the player sequence, making the two lists potentially out of alignment.
moveInQueue
Queue reordering follows different paths depending on shuffle mode:
| Mode | Strategy |
|---|---|
| Normal | _player.moveAudioSource(oldIndex, newIndex) moves the underlying audio source; the Cubit list is updated to match. |
| Shuffle | The drag indices address positions in the shuffled order. _player.moveAudioSource would move the wrong underlying sources and re-randomize the moved item’s position. Instead, the shuffle order itself is edited and _player.shuffle() is called with _shuffleOrder holding the desired result. |
clearCurrentSong
dispose
StreamSubscription objects before disposing the AudioPlayer, preventing stale callbacks after the service is torn down.
_ReorderableShuffleOrder
DefaultShuffleOrder.shuffle randomises the order. When the user drags a row in the queue screen during shuffle mode, the app already knows the exact desired order. applyOnNextShuffle caches that order so the very next _player.shuffle() call sets indices to the supplied list instead of randomising. The cached order is consumed and cleared on the first use, so subsequent unrelated shuffle calls behave normally.