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 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.

Layer Diagram

LayerExamplesRole
Screens / WidgetsHomeScreen, SongPlayedScreen, PlayingQueueScreenRender UI, watch Cubits, forward interactions to Cubits or services
Cubits (State)PlaybackStateCubit, LibraryCubit, UICubitHold immutable state; emit new states when data changes
Services (Business Logic)MusicOrchestratorService, JustAudioPlaybackService, FavoritesServiceOrchestrate cross-cubit workflows, own playback logic
Repositories (Data / Platform)AudioRepository, ArtworkRepository, PreferencesRepositoryAbstract platform APIs and persistence behind interfaces
Native Pluginsjust_audio, audio_service, on_audio_queryLow-level audio engine, media session, device media store
Data flows strictly downward (screens → cubits → services → repositories → plugins). Events and stream updates propagate back upward through Cubit state emissions and StreamSubscriptions wired in constructors.

Five Cubits

Musynth manages all runtime state through five flutter_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.
All five Cubits are registered in the 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.
ServiceResponsibility
MusicOrchestratorServiceCoordinates starting playback: selects the right playlist, calls PlaybackService, and updates PlaybackStateCubit
JustAudioPlaybackServiceWraps AudioPlayer and AudioPlayerHandler; listens to positionStream and currentIndexStream, emitting into AudioControlCubit
FavoritesServiceHandles toggling a favorite: updates FavoritesCubit state and persists IDs via PreferencesRepository
TagEditorServiceReads and writes embedded audio metadata (title, artist, cover art) via platform channels
FileManagementServiceDeletes 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.
InterfaceImplementationWhat it wraps
AudioRepositoryMusicQuerySelectorRepository (also implements ArtworkRepository)on_audio_query / music_query_selector plugin for device media store
ArtworkRepositorysame instance as AudioRepositoryNative color extraction and artwork decoding
PreferencesRepositorySharedPreferencesRepositoryshared_preferences plugin for persisting settings and favorites

Dependency Injection

All singletons are registered through GetIt.instance (aliased as setupLocator). Startup is split into two ordered phases:
  1. setupAudioHandlers() — registers infrastructure (audio player, repositories, caches) before AudioService.init.
  2. setupServiceLayer(...) — registers the already-created Cubit singletons and the service layer objects that depend on them.
Read the complete registration table in Dependency Injection.

Routing

Navigation is handled by a single GoRouter 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.

Build docs developers (and LLMs) love