audio_video_player 0.1.0-beta.6 copy "audio_video_player: ^0.1.0-beta.6" to clipboard
audio_video_player: ^0.1.0-beta.6 copied to clipboard

A comprehensive Flutter plugin for audio and video playback with playlists, albums, background audio, PiP, downloads and more.

audio_video_player #

A comprehensive Flutter plugin for audio and video playback.

Replaces media_kit with a tightly integrated stack built on first-class packages:

Concern Package
Audio playback just_audio
Background audio / lock-screen controls just_audio_background + audio_service
Audio session management audio_session
Video playback video_player
Video UI & controls chewie
Offline downloads background_downloader
PiP (Picture-in-Picture) Native (Android / iOS)
AirPlay / Chromecast Native stub + Dart bridge

Features #

  • Single-file and playlist/album audio with background playback and lock-screen / notification controls
  • Reactive streams for state, position, duration, queue, and current item
  • Shuffle, repeat (off / one / all), playback speed, and volume
  • Sleep timer with per-second countdown stream
  • Equalizer (Android) — per-band gain control with preset reset
  • Video player with full Chewie UI (full-screen, speed, subtitles)
  • Video playlist with auto-advance and repeat modes
  • Picture-in-Picture on Android and iOS
  • Offline downloads via background_downloader — progress streams, resume, delete
  • AirPlay (iOS) and Chromecast stub (Android) via CastController

Table of Contents #

  1. Installation
  2. Platform setup
  3. Initialisation
  4. Models
  5. Audio playback
  6. Sleep Timer
  7. Equalizer (Android)
  8. Video playback
  9. Picture-in-Picture
  10. Downloads
  11. Cast / AirPlay
  12. Running the example app

Installation #

Add the plugin to your project's pubspec.yaml:

From pub.dev (recommended):

dependencies:
  audio_video_player: ^0.1.0-beta.5

From a local path (during development):

dependencies:
  audio_video_player:
    path: ../audio_video_player

From a git repository:

dependencies:
  audio_video_player:
    git:
      url: https://github.com/Rameshwar-Amancha/audio_video_player
      ref: main

Then run:

flutter pub get

Platform setup #

Android #

Your app's android/app/src/main/AndroidManifest.xml must include the following permissions and activity attributes:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Required -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

    <application ...>
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:supportsPictureInPicture="true"           <!-- Required for PiP -->
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            ...>
            <!-- existing content -->
        </activity>
    </application>
</manifest>

Minimum SDK: The plugin requires minSdk 21 (Android 5.0). Ensure your android/app/build.gradle has minSdk = 21 or higher.

iOS #

In your app's ios/Runner/Info.plist add the background audio mode and allow HTTP streams during development:

<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
</array>

<!-- Allow cleartext HTTP for non-HTTPS streams (development only) -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

For production, remove NSAllowsArbitraryLoads and use HTTPS-only sources or add specific domain exceptions.

iOS deployment target: 13.0 or higher (set in Xcode under Runner → General → Minimum Deployments).


Initialisation #

Call AudioVideoPlayerPlugin.init() once, before runApp():

import 'package:audio_video_player/audio_video_player.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await AudioVideoPlayerPlugin.init(
    // Required – identifies the Android foreground-service notification channel.
    androidNotificationChannelId: 'com.yourapp.audio',
    androidNotificationChannelName: 'Music',

    // Optional
    androidNotificationIcon: 'mipmap/ic_launcher', // drawable resource
    notificationColor: Colors.deepPurple,          // Android notification colour
    pauseWhenDucked: false,                        // true = pause on nav-app focus loss
  );

  runApp(const MyApp());
}

AudioServiceWidget is not requiredjust_audio_background handles foreground-service lifecycle internally.


Models #

AVMediaItem #

The unified media descriptor used by both stacks:

const item = AVMediaItem(
  id: 'track_001',                          // unique ID (UUID recommended)
  uri: 'https://example.com/track.mp3',     // https://, file://, or asset://
  type: AVMediaType.audio,                  // or AVMediaType.video
  title: 'My Song',
  artist: 'Artist Name',
  album: 'Album Title',
  albumArtUri: 'https://example.com/art.jpg',
  duration: Duration(minutes: 3, seconds: 45),
);

URI schemes:

Scheme Example Use case
https:// https://cdn.example.com/track.mp3 Remote stream
file:// file:///data/user/0/.../track.mp3 Local file
asset:// asset://assets/audio/demo.mp3 Flutter asset

Playlist #

final playlist = Playlist(
  id: 'pl_001',
  name: 'My Playlist',
  description: 'Optional description',
  artUri: 'https://example.com/art.jpg',
  items: [item1, item2, item3],
);

// Mutations return a new Playlist (immutable pattern):
final updated = playlist.add(item4);
final reordered = playlist.reorder(0, 2); // move item at index 0 to index 2
final removed = playlist.remove('track_001');

Album #

final album = Album(
  id: 'alb_001',
  name: 'Greatest Hits',
  artist: 'Artist Name',
  year: 2026,
  artUri: 'https://example.com/cover.jpg',
  items: tracks,
);

Audio playback #

Single track #

final audio = AudioController();

// Remote URL
await audio.open(
  URISource(AVMediaItem(id: '1', uri: 'https://example.com/track.mp3', type: AVMediaType.audio, title: 'Track')),
  autoPlay: true,
);

// Flutter asset
await audio.open(
  AssetSource(
    AVMediaItem(id: '2', uri: 'asset://assets/audio/demo.mp3', type: AVMediaType.audio, title: 'Demo'),
    'assets/audio/demo.mp3',
  ),
  autoPlay: true,
);

// Local file
await audio.open(
  FileSource(
    AVMediaItem(id: '3', uri: 'file:///path/to/file.mp3', type: AVMediaType.audio, title: 'Local'),
    File('/path/to/file.mp3'),
  ),
  autoPlay: true,
);

Playlist & Album #

final audio = AudioController();

// Playlist (starts from item at index 2)
await audio.open(PlaylistSource(myPlaylist, initialIndex: 2), autoPlay: true);

// Album
await audio.open(AlbumSource(myAlbum), autoPlay: true);

// Queue management
await audio.skipToNext();
await audio.skipToPrevious();
await audio.skipToIndex(3);
await audio.add(newItem);          // append one item
await audio.addAll([item4, item5]); // append multiple items
await audio.removeAt(1);           // remove by index
await audio.move(0, 4);            // reorder: move index 0 to index 4
await audio.clear();               // empty the queue

Shuffle & Repeat #

await audio.setShuffleMode(true);
await audio.setRepeatMode(RepeatMode.all);   // off | one | all

Audio streams reference #

final audio = AudioController();

// Subscribe in initState / inside a StreamBuilder

audio.playerStateStream       // Stream<AVPlayerState>
audio.positionStream          // Stream<Duration>
audio.durationStream          // Stream<Duration?>
audio.bufferedPositionStream  // Stream<Duration>
audio.volumeStream            // Stream<double>        (0.0–1.0)
audio.speedStream             // Stream<double>
audio.repeatModeStream        // Stream<RepeatMode>
audio.shuffleEnabledStream    // Stream<bool>
audio.queueStream             // Stream<List<AVMediaItem>>
audio.currentItemStream       // Stream<AVMediaItem?>
audio.currentIndexStream      // Stream<int?>

AVPlayerState values: idleloadingreadyplayingpausedcompletederror

Audio controls reference #

await audio.play();
await audio.pause();
await audio.stop();
await audio.seek(Duration(seconds: 90));  // AudioController uses seek()
await audio.setVolume(0.8);               // 0.0–1.0
await audio.setSpeed(1.5);               // e.g. 0.5, 1.0, 1.5, 2.0
await audio.skipToNext();
await audio.skipToPrevious();
await audio.skipToIndex(2);              // 0-based queue index
await audio.add(item);                   // append to queue
await audio.addAll([item2, item3]);      // append multiple
await audio.removeAt(1);                 // remove by index
await audio.move(0, 3);                  // reorder
await audio.clear();                     // empty the queue
await audio.setShuffleMode(true);
await audio.setRepeatMode(RepeatMode.one);

// Sync getters
audio.playerState    // AVPlayerState
audio.position       // Duration
audio.duration       // Duration?
audio.volume         // double
audio.speed          // double
audio.repeatMode     // RepeatMode
audio.shuffleEnabled // bool
audio.queue          // List<AVMediaItem>
audio.currentItem    // AVMediaItem?
audio.currentIndex   // int?
audio.isPlaying      // bool

// Always dispose when done
await audio.dispose();

Example widget #

class AudioPlayerWidget extends StatefulWidget { ... }

class _AudioPlayerWidgetState extends State<AudioPlayerWidget> {
  final _audio = AudioController();

  @override
  void initState() {
    super.initState();
    _audio.open(URISource(myItem), autoPlay: true);
  }

  @override
  void dispose() {
    _audio.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<AVPlayerState>(
      stream: _audio.playerStateStream,
      builder: (context, snapshot) {
        final state = snapshot.data ?? AVPlayerState.idle;
        return IconButton(
          icon: Icon(state == AVPlayerState.playing ? Icons.pause : Icons.play_arrow),
          onPressed: state == AVPlayerState.playing ? _audio.pause : _audio.play,
        );
      },
    );
  }
}

Sleep Timer #

The SleepTimer is already wired to the AudioController — access it via audio.sleepTimer:

final audio = AudioController();

// Start a 30-minute sleep timer
audio.sleepTimer.start(const Duration(minutes: 30));

// Cancel early
audio.sleepTimer.cancel();

// Show countdown in the UI
StreamBuilder<Duration?>(
  stream: audio.sleepTimer.remainingStream,
  builder: (context, snap) {
    final remaining = snap.data;
    if (remaining == null) return const Text('No sleep timer');
    final m = remaining.inMinutes.remainder(60).toString().padLeft(2, '0');
    final s = remaining.inSeconds.remainder(60).toString().padLeft(2, '0');
    return Text('Sleep in $m:$s');
  },
);

Equalizer (Android) #

Available on Android only. Access via audio.equalizer:

final audio = AudioController();
final eq = audio.equalizer;

if (eq.isAvailable) {
  // Enable
  await eq.setEnabled(true);

  // Get bands and adjust gain
  final bands = await eq.getBands();
  for (final band in bands) {
    print('${band.centerFrequency} Hz: ${band.gain} dB');
  }

  // Set 60 Hz band to +3 dB
  await eq.setBandGain(0, 3.0);

  // Reset all bands to 0 dB
  await eq.reset();

  // Disable
  await eq.setEnabled(false);
}

Video playback #

Single video #

final vc = VideoController(
  autoPlay: true,
  looping: false,
  allowFullScreen: true,
  allowPlaybackSpeedChanging: true,
);

await vc.open(AVMediaItem(
  id: 'v1',
  uri: 'https://example.com/video.mp4',
  type: AVMediaType.video,
  title: 'My Video',
));

In your widget, pass vc.chewieController to a Chewie widget:

import 'package:chewie/chewie.dart';

@override
Widget build(BuildContext context) {
  final chewieController = vc.chewieController;
  if (chewieController == null) return const CircularProgressIndicator();
  return AspectRatio(
    aspectRatio: vc.videoPlayerController!.value.aspectRatio,
    child: Chewie(controller: chewieController),
  );
}

Video controls:

await vc.play();
await vc.pause();
await vc.seekTo(Duration(seconds: 30));
await vc.setVolume(0.5);
await vc.setSpeed(1.25);
await vc.dispose();

Video playlist #

final vpc = VideoPlaylistController(
  items: myAlbum.items,   // List<AVMediaItem>
  initialIndex: 0,
  autoPlay: true,
  repeatMode: RepeatMode.all,
);

await vpc.start();

// Navigation
await vpc.skipToNext();
await vpc.skipToPrevious();
await vpc.skipToIndex(2);

// Queue management
vpc.add(newItem);                   // append item
await vpc.remove('track_id_here'); // remove by AVMediaItem.id

// Expose in widget
StreamBuilder<int>(
  stream: vpc.currentIndexStream,
  builder: (context, snap) {
    final ctrl = vpc.currentController;
    if (ctrl?.chewieController == null) return const SizedBox();
    return Chewie(controller: ctrl!.chewieController!);
  },
);

await vpc.dispose();

Picture-in-Picture #

PipController is accessible via VideoController.pip:

final vc = VideoController();
await vc.open(myVideoItem);

// Check availability
final supported = await vc.pip.isPipAvailable();

// Enter PiP (Android – typically called from onPause or a UI button)
if (supported) {
  await vc.pip.enterPip();
}

// Exit PiP (iOS only; Android handles this via the system back gesture)
await vc.pip.exitPip();

Android: PiP triggers when the user presses the Home button while the app activity has android:supportsPictureInPicture="true". Call enterPip() from your FlutterActivity's onUserLeaveHint() override, or from a UI button.

iOS: PiP is managed automatically by AVPictureInPictureController. Call enterPip() to activate it programmatically.


Downloads #

final dl = DownloadManager.instance;

// Download a track (returns a stream of progress events synchronously)
final stream = dl.download(myItem);
stream.listen((progress) {
  if (progress.isComplete) {
    print('Downloaded! local path in item.extras["localPath"]');
  } else {
    print('${(progress.progress * 100).toStringAsFixed(0)}%');
  }
});

// Check / use downloaded file
final isDownloaded = await dl.isDownloaded(myItem);
final downloaded = await dl.getDownloadedItems();  // List<AVMediaItem> with localPath set

// Play a downloaded item offline (localPath is injected into extras)
final localItem = downloaded.first;
final localUri = localItem.extras['localPath'] as String;
final offlineItem = localItem.copyWith(uri: 'file://$localUri', isLocal: true);
await audio.open(URISource(offlineItem), autoPlay: true);

// Cancel / delete
await dl.cancel(myItem.id);          // cancel an in-progress download
await dl.deleteDownload(myItem.id); // delete a completed local file

Downloaded files are stored under:
<appDocuments>/audio_video_player/downloads/<itemId>.<ext>


Cast / AirPlay #

final cast = CastController();

// iOS – show the system AirPlay route picker sheet
await cast.showAirPlayRoutePicker();

// Android – Chromecast (requires Google Cast SDK integration in host app)
final available = await cast.isAvailable();
if (available) {
  final devices = await cast.discoverDevices();
  await cast.connect(devices.first);
  await cast.cast(myItem.uri);
  await cast.disconnect();
}

Chromecast on Android requires manually adding the Google Cast SDK to your host app. See android/src/main/kotlin/.../CastHandler.kt for setup instructions.


Running the example app #

Prerequisites #

Requirement Version
Flutter SDK ≥ 3.27.0
Dart SDK ≥ 3.9.0
Android device / emulator API 21+ (Android 5)
iOS device / simulator iOS 13+

1. Install dependencies #

From the plugin root:

cd audio_video_player
flutter pub get

cd example
flutter pub get

2. Verify your device is connected #

flutter devices

You should see your physical device or emulator listed, e.g.:

sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64  • Android 14 (API 34)
Pixel 7 (mobile)             • ZX12345678   • android-arm64  • Android 14 (API 34)

3. Run on your attached Android device #

From the example/ directory:

cd example
flutter run

To target a specific device by ID:

flutter run -d ZX12345678

To run a release build (better performance, tests background audio properly):

flutter run --release

4. Run on iOS #

flutter run -d iPhone   # or use the simulator device ID from flutter devices

5. What the example app demonstrates #

Screen How to trigger
Single Audio Tap "Single Audio Track" — plays a remote MP3 with play/pause/seek controls
Audio Playlist Tap "Audio Playlist" — queue of 3 tracks with shuffle, repeat, sleep timer
Video Player Tap "Video Player" — Chewie UI with full-screen, speed selector, PiP button

Testing background audio (Android):

  1. Open the Audio Playlist screen and start playback
  2. Press the Home button — audio continues in the background
  3. Pull down the notification shade — media controls appear on the lock screen
  4. Swipe the notification to stop playback

Testing Picture-in-Picture (Android):

  1. Open the Video Player screen and start a video
  2. Tap the PiP button (bottom-right of the video) or press the Home button
  3. Video shrinks into the system PiP window

API quick reference #

Classes #

Class Purpose
AudioVideoPlayerPlugin One-time plugin initialiser — call .init() in main()
AVMediaItem Unified media descriptor (audio + video)
Playlist Ordered collection of AVMediaItems
Album Playlist subtype with artist and year
MediaSource Sealed: URISource, FileSource, AssetSource, PlaylistSource, AlbumSource
AudioController Full audio playback controller (streams, queue, equalizer, sleep timer)
SleepTimer Countdown timer that pauses AudioController
EqualizerController Android-only per-band EQ (access via AudioController.equalizer)
VideoController Single video + Chewie integration + PiP
VideoPlaylistController Video queue with auto-advance
PipController Enter/exit native Picture-in-Picture (access via VideoController.pip)
DownloadManager Offline download queue with progress streams
CastController Chromecast (Android stub) + AirPlay (iOS) bridge

Enums #

Enum Values
AVMediaType audio, video
AVPlayerState idle, loading, ready, playing, paused, completed, error
RepeatMode off, one, all
CastDeviceType chromecast, airPlay, unknown
1
likes
150
points
68
downloads

Documentation

API reference

Publisher

verified publisherrameshwaramancha.com

Weekly Downloads

A comprehensive Flutter plugin for audio and video playback with playlists, albums, background audio, PiP, downloads and more.

Homepage

License

MIT (license)

Dependencies

audio_service, audio_session, background_downloader, chewie, flutter, flutter_cache_manager, http, just_audio, just_audio_background, path, path_provider, plugin_platform_interface, rxdart, video_player

More

Packages that depend on audio_video_player

Packages that implement audio_video_player