Musynth is a Flutter music player whose codebase is organized into five horizontal layers. Each layer has a single responsibility and communicates only with the layer immediately below it, keeping widgets free of business logic and keeping platform code isolated from the UI.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.
Layer Diagram
| Layer | Examples | Role |
|---|---|---|
| Screens / Widgets | HomeScreen, SongPlayedScreen, PlayingQueueScreen | Render UI, watch Cubits, forward interactions to Cubits or services |
| Cubits (State) | PlaybackStateCubit, LibraryCubit, UICubit | Hold immutable state; emit new states when data changes |
| Services (Business Logic) | MusicOrchestratorService, JustAudioPlaybackService, FavoritesService | Orchestrate cross-cubit workflows, own playback logic |
| Repositories (Data / Platform) | AudioRepository, ArtworkRepository, PreferencesRepository | Abstract platform APIs and persistence behind interfaces |
| Native Plugins | just_audio, audio_service, on_audio_query | Low-level audio engine, media session, device media store |
StreamSubscriptions wired in constructors.
Five Cubits
Musynth manages all runtime state through fiveflutter_bloc Cubits, each covering a distinct concern:
PlaybackStateCubit
Tracks the currently playing
SongModel, the active playlist, whether shuffle is on, and the playing/paused boolean.AudioControlCubit
Tracks the live playback position (
currentDuration) and the current track index within the active playlist.LibraryCubit
Owns the full media library: songs, albums, artists, genres, playlists, and lazily-populated per-album/artist/genre collections.
FavoritesCubit
Maintains the list of favorite songs and exposes
isFavoriteSong(id) for fast per-song checks anywhere in the UI.UICubit
Derives and caches the dominant color extracted from each album’s artwork, the current hero animation ID, and color lookup map.
GetIt service locator after creation, and exposed to the widget tree via MultiBlocProvider in MyApp. See State Management for the full breakdown.
Service Layer
Services sit between Cubits and repositories. They contain the orchestration logic that would otherwise sprawl across widgets.| Service | Responsibility |
|---|---|
MusicOrchestratorService | Coordinates starting playback: selects the right playlist, calls PlaybackService, and updates PlaybackStateCubit |
JustAudioPlaybackService | Wraps AudioPlayer and AudioPlayerHandler; listens to positionStream and currentIndexStream, emitting into AudioControlCubit |
FavoritesService | Handles toggling a favorite: updates FavoritesCubit state and persists IDs via PreferencesRepository |
TagEditorService | Reads and writes embedded audio metadata (title, artist, cover art) via platform channels |
FileManagementService | Deletes and manages audio files on device storage |
Repository Layer
Repositories provide stable Dart interfaces over platform-specific implementations, making it straightforward to swap backends in tests or on a new platform.| Interface | Implementation | What it wraps |
|---|---|---|
AudioRepository | MusicQuerySelectorRepository (also implements ArtworkRepository) | on_audio_query / music_query_selector plugin for device media store |
ArtworkRepository | same instance as AudioRepository | Native color extraction and artwork decoding |
PreferencesRepository | SharedPreferencesRepository | shared_preferences plugin for persisting settings and favorites |
Dependency Injection
All singletons are registered throughGetIt.instance (aliased as setupLocator). Startup is split into two ordered phases:
setupAudioHandlers()— registers infrastructure (audio player, repositories, caches) beforeAudioService.init.setupServiceLayer(...)— registers the already-created Cubit singletons and the service layer objects that depend on them.
Routing
Navigation is handled by a singleGoRouter instance (AppRouter.router) with eight named routes. Screens are reached with context.pushNamed(AppRoutes.xxx). The “now playing” screen (/song-played) uses a bottom-to-top SlideTransition; all other routes fade in.
See Routing for the full route table, path parameters, and transition details.