Musynth’s playback system is built aroundDocumentation 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.
SongPlayedScreen — a full-screen Now Playing view that slides up over the home screen — backed by AudioService and AudioPlayerHandler for persistent background playback. This page covers every control available to the listener, the queue view, and the technical plumbing that ties them together.
Starting playback
All playback entry points funnel throughMusicOrchestratorService, which resolves the correct song list for the chosen context, loads it into just_audio, and starts the player.
| Parameter | Purpose |
|---|---|
song | The track to play first |
type | Which PlaylistType to resolve as the queue (e.g. PlaylistType.album) |
heroId | Flutter Hero tag so the artwork animates smoothly from list to Now Playing |
id | Collection ID (album/artist/genre/playlist) when type is not songs or favorites |
activateShuffle | When true, enables shuffle immediately (used by the FAB shuffle action) |
MusicOrchestratorService.initSong(song, {heroId}) restores the last-played track and seeks to the saved position without auto-playing.
Now Playing screen (SongPlayedScreen)
The screen is pushed via a slide-up route transition and uses opaque: false so the home screen remains visible through the closing slide animation (avoiding a black flash). The scaffold background is Colors.transparent; the visual backdrop is produced by blurring and tinting the album artwork using ImageFiltered.
Dynamic theming
When a song starts, the dominant colour is extracted natively from the artwork (viaqueryArtworkColor — androidx.palette on Android, Core Image on iOS). That colour propagates through the local Theme to typography, icons, and the progress bar, so the Now Playing screen always feels visually cohesive with the current artwork.
Orientations
The screen has fully adapted portrait and landscape layouts. In landscape mode the artwork sits in a fixed-width column on the left; controls and metadata fill the right panel.Swipeable artwork carousel
The artwork area is aPageView over the current queue (PlaybackStateCubit.currentPlaylist). Each page shows the album art for one track.
- Swipe left/right to skip to the adjacent song. The swipe calls
AudioPlayer.seek(Duration.zero, index: newIndex)and resumes playback. - External song changes (skip buttons, auto-advance) animate the carousel to the correct page via
animateToPage, keeping the two in sync without fighting an in-progress user drag. - Volume via vertical drag — dragging up/down on the artwork adjusts system volume through the
volume_controllerpackage. A translucent pill overlay shows the current level and icon.
A stale-result race condition is avoided by tracking a
_userSwipe latch: onPageChanged only seeks when the page change originated from a real user drag (ScrollStartNotification.dragDetails != null). Programmatic animateToPage calls never trigger a seek.Playback controls
| Control | Behaviour |
|---|---|
| Play / Pause | An AnimatedIcon (play ↔ pause) with a 200 ms AnimationController. Streams AudioPlayer.playingStream to stay in sync with the player state. |
| Skip next / previous | Tap = clean skip to the next or previous track. Long-press = progressive ±10 s scrub (500 ms delay before the first seek, then every 500 ms while held). |
| Shuffle | Toggles AudioPlayer.setShuffleModeEnabled. The icon opacity indicates state: full-opacity = on, dimmed = off. |
| Repeat / Loop | Cycles through LoopMode.off → LoopMode.one → LoopMode.all with matching icons (Icons.repeat, Icons.repeat_one, Icons.keyboard_double_arrow_right). A toast confirms the new mode. |
Skip long-press scrub
The press-and-hold scrub is implemented with aListener widget that polls every 500 ms while the pointer is down, reading the live player position and seeking by ±10 s. A 500 ms initial delay ensures a quick tap always fires a clean skip.
Progress bar and seek scrubbing
The_SongTimeline widget wraps the audio_video_progress_bar package’s ProgressBar, displaying elapsed and total time with a draggable thumb.
AudioPlayer.seek directly, jumping playback to the chosen position with no additional debouncing.
Favourite button
AIcons.favorite / Icons.favorite_border button in the Now Playing screen calls FavoritesService.toggle(song, ...). The same toggle is also available from:
- The
MoreSongOptionsModal(song context menu, accessible from any list). - The persistent media notification (custom
favoriteaction).
FavoritesCubit is a Bloc whose state is watched directly:
Playing queue (PlayingQueueScreen)
Tap the queue icon (top-right of the Now Playing screen) to open the full queue view.
Drag to reorder
Each row has a
ReorderableDelayedDragStartListener drag handle. Releasing calls PlaybackService.moveInQueue(oldIndex, newIndex) to update the live player order.Remove from queue
Tap the
Icons.remove_circle_outline button on a row. Calls PlaybackService.removeFromQueue(song).Background playback and media notification
Musynth uses theaudio_service package with a custom AudioPlayerHandler. Once a song starts, a persistent system notification appears with:
| Action | Button |
|---|---|
| Play / Pause | ▶ / ⏸ |
| Skip previous | ⏮ |
| Skip next | ⏭ |
| Toggle favourite | ♥ (custom action) |
| Dismiss / Stop | ✕ |
Mini-player bar (CurrentSongTile)
A compact now-playing strip is docked to the bottom of every main screen while a song is loaded. It shows the artwork, title, and a play/pause button. Tapping it navigates to the full SongPlayedScreen.