8000
Skip to content

Noir Player is a lightweight Flutter music player supporting background playback, local media querying, and a responsive UI. It integrates audio_service, just_audio, and on_audio_query for a seamless experience. Features include a tabbed library, a "Now Playing" screen, and background audio.

License

Notifications You must be signed in to change notification settings

Abdullah-Masood-05/NoirPlayer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

45 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Noir Player

Noir Player is a lightweight Flutter music player that demonstrates how to:

  • Initialise the Android audio service with background playback support
  • Query the device’s media library using on_audio_query
  • Play, pause, stop and show the current media item in a dedicated β€œNow Playing” screen
  • Keep the UI responsive with StreamBuilders

Table of Contents


πŸ“¦ Overview

Noir Player is a small, cross‑platform Flutter app that:

  1. Loads all local audio files from the device’s library.
  2. Shows them in a tabbed library (All, Albums, Artists, …).
  3. Initialises a background audio service (so playback keeps going while the app is backgrounded).
  4. Plays a selected track and navigates to a β€œNow Playing” screen that displays title, artist, album art and playback controls.

✨ Features

Feature File How it works
Home Screen home_screen.dart Simple drawer β†’ LibraryScreen
Tab‑based Library library_screen.dart Uses QueryArtworkWidget & OnAudioQuery
Audio Service audio_handler.dart Wraps audio_service & just_audio
Now Playing UI player_screen.dart Consumes audioHandler.mediaItem & audioHandler.playbackState
Background playback audio_service + just_audio Keeps the music playing when the user leaves the app or locks the device

πŸ“ Project Structure

lib/
β”œβ”€β”€ main.dart
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   └── audio_handler.dart
β”‚   └── theme/
β”‚       └── app_theme.dart
β”œβ”€β”€ screens/
β”‚   β”œβ”€β”€ home/
β”‚   β”‚   └── home_screen.dart
β”‚   β”œβ”€β”€ library/
β”‚   β”‚   β”œβ”€β”€ tabs/
β”‚   β”‚   β”‚   β”œβ”€β”€ albums_tab.dart
β”‚   β”‚   β”‚   β”œβ”€β”€ artists_tab.dart
β”‚   β”‚   β”‚   β”œβ”€β”€ playlists_tab.dart
β”‚   β”‚   β”‚   └── songs_tab.dart
β”‚   β”‚   └── library_screen.dart
β”‚   β”œβ”€β”€ player/
β”‚   β”‚   └── player_screen.dart
β”‚   β”œβ”€β”€ playlists/
β”‚   β”‚   β”œβ”€β”€ playlists_screen.dart
β”‚   β”‚   └── playlist_songs_screen.dart
β”‚   β”œβ”€β”€ albums/
β”‚   β”‚   └── album_songs_screen.dart
β”‚   β”œβ”€β”€ artist/
β”‚   β”‚   └── artist_songs_screen.dart
β”‚   └── settings/
β”‚       └── settings_screen.dart
β”œβ”€β”€ widgets/
β”‚   └── query_artwork_widget.dart
└── ...

Note: The lib/core/services/audio_handler.dart file contains the core of the audio service (initialisation, play, pause, stop, media item updates).


πŸš€ Getting Started

Prerequisites

Platform Requirement
Android Flutter SDK β‰₯ 2.18, Android 6.0+
  • Make sure you have a recent version of Flutter installed:
    flutter --version
  • For Android you’ll need the READ_EXTERNAL_STORAGE permission in AndroidManifest.xml.
    Noir Player already includes the permission request flow via on_audio_query.

Installation

git clone https://github.com/your‑username/noir_player.git
cd noir_player
flutter pub get

Running the App

# Android
flutter run -d android

> On first launch, the app will request permission to read the device’s music library.  
> Grant the permission and the library will populate automatically.

🧭 Workflow

High‑level Flow Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Noir Player UI  β”‚
β”‚ (main.dart)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚ init
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ AudioServiceManager  β”‚
β”‚ (audio_handler.dart) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚ start
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Query Audio Files  β”‚
β”‚ (on_audio_query)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚ fetch list
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   LibraryScreen       β”‚
β”‚ (library_screen.dart) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚ tab navigation
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PlayerScreen          β”‚
β”‚ (player_screen.dart)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Each arrow represents a stream or event (e.g., audioHandler.mediaItem, audioHandler.playbackState).
The UI listens to these streams and updates automatically.

Step‑by‑Step Walk‑through

# User Action App Reaction Code Path
1 Launch app main.dart β†’ initAudioService() β†’ LibraryScreen main.dart
2 Open drawer β†’ tap β€œLibrary” LibraryScreen is pushed onto the Navigator stack home_screen.dart
3 LibraryScreen appears Tabs load (All / Artists / Albums). Each tab fetches tracks via on_audio_query and shows a list library_screen.dart
4 Tap a song audioHandler.play(Song) is called, which:
β€’ Updates MediaItem stream
β€’ Calls JustAudio.setFilePath()
β€’ Starts playback
audio_handler.dart
5 UI updates StreamBuilder<MediaItem?> on PlayerScreen shows title/artist/album art player_screen.dart
6 Play/Pause button audioHandler.play() / audioHandler.pause() toggles playback state player_screen.dart
7 Stop/Back button audioHandler.stop() & Navigator.pop() player_screen.dart
8 Close app Background audio continues due to audio_service configuration audio_handler.dart

🧱 Architecture Details

main.dart

import 'package:flutter/material.dart';
import '../core/services/audio_handler.dart';

void main() {
  runApp(const NoirPlayerApp());
}

class NoirPlayerApp extends StatelessWidget {
  const NoirPlayerApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Noir Player',
      theme: ThemeData.dark(),
      home: const HomeScreen(),
      routes: {
        '/library': (_) => const LibraryScreen(),
        '/player': (_) => const PlayerScreen(),
      },
    );
  }
}


*Bootstraps the app, initialises the audio service in `main()` and defines the navigation routes.*

### `library_screen.dart`

```dart
class LibraryScreen extends StatefulWidget {
  const LibraryScreen({super.key});
  ...
}

class _LibraryScreenState extends State<LibraryScreen> {
  // TabController is used to switch between "All", "Artists", "Albums" tabs.
  // Each tab uses `on_audio_query` to fetch the relevant media.
}

Shows a 4‑tabbed view of the local library and provides a navigation button to the β€œNow Playing” screen.

audio_handler.dart

import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
import 'package:on_audio_query/on_audio_query.dart';

Future<void> initAudioService() async {
  await AudioService.start(
    backgroundTaskEntrypoint: () => AudioServiceBackground.run(() => MyAudioTask()),
    androidNotificationChannelName: 'Noir Player',
    androidNotificationIcon: 'mipmap/ic_launcher',
    androidStopForegroundOnPause: false,
  );
}

Wraps the complex background audio initialization logic.
MyAudioTask extends BackgroundAudioTask (not shown in the snippet) and provides the playerStateStream, mediaItem and playback controls.

player_screen.dart

class PlayerScreen extends StatefulWidget {
  const PlayerScreen({super.key});
  ...
}

class _PlayerScreenState extends State<PlayerScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<MediaItem?>(
        stream: audioHandler.mediaItem,
        builder: (_, snapshot) => ...
      ),
      bottomNavigationBar: StreamBuilder<PlaybackState>(
        stream: audioHandler.playbackState,
        builder: (_, snapshot) => ...
      ),
    );
  }
}

Observes the global audio service streams and shows the currently playing track, its artwork, and the playback controls.


πŸ“¦ Dependencies

Package Purpose Version
flutter SDK β‰₯ 2.18
just_audio Lightweight audio playback ^0.9.27
audio_service Background audio + notification handling ^0.18.7
on_audio_query Read device’s music library & artwork ^2.5.0
permission_handler Request storage permission on Android ^10.2.0

All packages are declared in pubspec.yaml.
Run flutter pub get to install them.


🀝 Contributing

Pull requests are welcome!
Please open an issue first to discuss any major changes or new features.


πŸ“„ License

MIT Β© 2024 Noir Player.
See LICENSE for details.


Enjoy building your own music player with Noir Player!

About

Noir Player is a lightweight Flutter music player supporting background playback, local media querying, and a responsive UI. It integrates audio_service, just_audio, and on_audio_query for a seamless experience. Features include a tabbed library, a "Now Playing" screen, and background audio.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published
0