assets_audio_player_plus 3.2.1
assets_audio_player_plus: ^3.2.1 copied to clipboard
Play music/audio stored in assets files directly from Flutter & Network, Radio, LiveStream, Local files. Compatible with Android, iOS, and macOS.
๐ง assets_audio_player_plus ๐ #
๐ข Project status #
This repository is a continuation of the original
assets_audio_playerplugin by Florent Champigny. It is actively maintained by Zakria Khan.Recent work includes restoring web support (migrated from
dart:htmltopackage:web+dart:js_interopfor WebAssembly compatibility), modernizing the example app to Material 3, and fixing a number of long-standing bugs.Pull requests are welcome. Please read CONTRIBUTING.md before opening one.
โ Platform test status #
Platform Status Notes ๐ Web (Chrome, JS) โ Tested All 14 demos verified end-to-end ๐ Web (WASM) โ Tested flutter build web --wasmclean๐ค Android โ Tested Emulator SDK 36, API channels all OK ๐ iOS โณ Pending To be tested soon ๐ฅ๏ธ macOS โณ Pending To be tested soon ๐ง Linux โณ Pending Community contributions welcome ๐ช Windows โณ Pending Community contributions welcome ๐ฆ Repo: github.com/cyclone-pk/assets_audio_player_plus
Found a bug on a tested platform? Open an issue with the demo that fails and your
flutter doctor -voutput.
Play music/audio stored in assets files (simultaneously) directly from Flutter (android / ios / web / macos).
You can also use play audio files from network using their url, radios/livestream and local files
Notification can be displayed on Android & iOS, and bluetooth actions are handled
flutter:
assets:
- assets/audios/
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/song1.mp3"),
autoStart: true,
showNotification: true,
);
๐ฅ Import #
dependencies:
assets_audio_player_plus: ^3.2.0
Works with flutter: ">=3.3.0", be sure to upgrade your sdk
| Audio Source | Android | iOS | Web | MacOS |
|---|---|---|---|---|
| ๐๏ธ Asset file (asset path) | โ | โ | โ | โ |
| ๐ Network file (url) | โ | โ | โ | โ |
| ๐ Local file (path) | โ | โ | โ | โ |
| ๐ป Network LiveStream / radio (url) (Default, HLS, Dash, SmoothStream) |
โ | โ | โ | โ |
| Feature | Android | iOS | Web | MacOS |
|---|---|---|---|---|
| ๐ถ Multiple players | โ | โ | โ | โ |
| ๐ฝ Open Playlist | โ | โ | โ | โ |
| ๐ฌSystem notification | โ | โ | ๐ซ | ๐ซ |
| ๐ง Bluetooth actions | โ | โ | ๐ซ | ๐ซ |
| ๐ Respect System silent mode | โ | โ | ๐ซ | ๐ซ |
| ๐ Pause on phone call | โ | โ | ๐ซ | ๐ซ |
| Commands | Android | iOS | Web | MacOS |
|---|---|---|---|---|
| โถ Play | โ | โ | โ | โ |
| โธ Pause | โ | โ | โ | โ |
| โน Stop | โ | โ | โ | โ |
| โฉ Seek(position) | โ | โ | โ | โ |
| โชโฉ SeekBy(position) | โ | โ | โ | โ |
| โฉ Forward(speed) | โ | โ | โ | โ |
| โช Rewind(speed) | โ | โ | โ | โ |
| โญ Next | โ | โ | โ | โ |
| โฎ Prev | โ | โ | โ | โ |
| Widgets | Android | iOS | Web | MacOS |
|---|---|---|---|---|
| ๐ฆ Audio Widget | โ | โ | โ | โ |
| ๐ฆ Widget Builders | โ | โ | โ | โ |
| ๐ฆ AudioPlayer Builders Extension | โ | โ | โ | โ |
| Properties | Android | iOS | Web | MacOS |
|---|---|---|---|---|
| ๐ Loop | โ | โ | โ | โ |
| ๐ Shuffle | โ | โ | โ | โ |
| ๐ get/set Volume | โ | โ | โ | โ |
| โฉ get/set Play Speed | โ | โ | โ | โ |
| โฉ get/set Pitch | โ | ๐ซ | ๐ซ | ๐ซ |
| Listeners | Android | iOS | Web | MacOS |
|---|---|---|---|---|
| ๐ฆป Listener onReady(completeDuration) | โ | โ | โ | โ |
| ๐ฆป Listener currentPosition | โ | โ | โ | โ |
| ๐ฆป Listener finished | โ | โ | โ | โ |
| ๐ฆป Listener buffering | โ | โ | โ | โ |
| ๐ฆป Listener volume | โ | โ | โ | โ |
| ๐ฆปListener Play Speed | โ | โ | โ | โ |
| ๐ฆปListener Pitch | โ | ๐ซ | ๐ซ | ๐ซ |
๐ Import assets files #
No needed to copy songs to a media cache, with assets_audio_player you can open them directly from the assets.
- Create an audio directory in your assets (not necessary named "audios")
- Declare it inside your pubspec.yaml
flutter:
assets:
- assets/audios/
๐ ๏ธ Getting Started #
final assetsAudioPlayer = AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio("assets/audios/song1.mp3"),
);
You can also play network songs from url
final assetsAudioPlayer = AssetsAudioPlayer();
try {
await assetsAudioPlayer.open(
Audio.network("http://www.mysite.com/myMp3file.mp3"),
);
} catch (t) {
//mp3 unreachable
}
LiveStream / Radio from url
The main difference with network, if you pause/play, on livestream it will resume to present duration
final assetsAudioPlayer = AssetsAudioPlayer();
try {
await assetsAudioPlayer.open(
Audio.liveStream(MY_LIVESTREAM_URL),
);
} catch (t) {
//stream unreachable
}
And play songs from file
//create a new player
final assetsAudioPlayer = AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio.file(FILE_URI),
);
for file uri, please look at https://pub.dev/packages/path_provider
assetsAudioPlayer.playOrPause();
assetsAudioPlayer.play();
assetsAudioPlayer.pause();
assetsAudioPlayer.seek(Duration to);
assetsAudioPlayer.seekBy(Duration by);
assetsAudioPlayer.forwardRewind(double speed);
//if positive, forward, if negative, rewind
assetsAudioPlayer.stop();
Notifications #
on iOS, it will use MPNowPlayingInfoCenter
- Add metas inside your audio
final audio = Audio.network("/assets/audio/country.mp3",
metas: Metas(
title: "Country",
artist: "Florent Champigny",
album: "CountryAlbum",
image: MetasImage.asset("assets/images/country.jpg"), //can be MetasImage.network
),
);
- open with
showNotification: true
_player.open(audio, showNotification: true)
Custom notification #
Custom icon (android only)
By ResourceName #
Make sure you added those icons inside your android/res/drawable !!! not on flutter assets !!!!
await _assetsAudioPlayer.open(
myAudio,
showNotification: true,
notificationSettings: NotificationSettings(
customStopIcon: AndroidResDrawable(name: "ic_stop_custom"),
customPauseIcon: AndroidResDrawable(name:"ic_pause_custom"),
customPlayIcon: AndroidResDrawable(name:"ic_play_custom"),
customPrevIcon: AndroidResDrawable(name:"ic_prev_custom"),
customNextIcon: AndroidResDrawable(name:"ic_next_custom"),
)
And don't forget tell proguard to keep those resources for release mode
(part Keeping Resources)
https://sites.google.com/a/android.com/tools/tech-docs/new-build-system/resource-shrinking
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/ic_next_custom, @drawable/ic_prev_custom, @drawable/ic_pause_custom, @drawable/ic_play_custom, @drawable/ic_stop_custom"/>
By Manifest #
-
Add your icon into your android's
resfolder (android/app/src/main/res) -
Reference this icon into your AndroidManifest (android/app/src/main/AndroidManifest.xml)
<meta-data
android:name="assets.audio.player.notification.icon"
android:resource="@drawable/ic_music_custom"/>
You can also change actions icons
<meta-data
android:name="assets.audio.player.notification.icon.play"
android:resource="@drawable/ic_play_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.pause"
android:resource="@drawable/ic_pause_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.stop"
android:resource="@drawable/ic_stop_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.next"
android:resource="@drawable/ic_next_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.prev"
android:resource="@drawable/ic_prev_custom"/>
Handle notification click (android) #
Add in main
AssetsAudioPlayer.setupNotificationsOpenAction((notification) {
//custom action
return true; //true : handled, does not notify others listeners
//false : enable others listeners to handle it
});
Then if you want a custom action on widget
AssetsAudioPlayer.addNotificationOpenAction((notification) {
//custom action
return false; //true : handled, does not notify others listeners
//false : enable others listeners to handle it
});
Custom actions #
You can enable/disable a notification action
open(AUDIO,
showNotification: true,
notificationSettings: NotificationSettings(
prevEnabled: false, //disable the previous button
//and have a custom next action (will disable the default action)
customNextAction: (player) {
print("next");
}
)
)
Update audio's metas / notification content #
After your audio creation, just call
audio.updateMetas(
player: _assetsAudioPlayer, //add the player if the audio is actually played
title: "My new title",
artist: "My new artist",
//if I not provide a new album, it keep the old one
image: MetasImage.network(
//my new image url
),
);
Bluetooth Actions #
You have to enable notification to make them work
Available remote commands :
- Play / Pause
- Next
- Prev
- Stop
HeadPhone Strategy #
(Only for Android for now)
while opening a song/playlist, add a strategy
assetsAudioPlayer.open(
...
headPhoneStrategy: HeadPhoneStrategy.pauseOnUnplug,
//headPhoneStrategy: HeadPhoneStrategy.none, //default
//headPhoneStrategy: HeadPhoneStrategy.pauseOnUnplugPlayOnPlug,
)
If you want to make it work on bluetooth too, you'll have to add the BLUETOOTH permission inside your AndroidManifest.xml
<uses-permission android:name="android.permission.BLUETOOTH" />
โ Play in parallel / simultaneously #
You can create new AssetsAudioPlayer using AssetsAudioPlayer.newPlayer(), which will play songs in a different native Media Player
This will enable to play two songs simultaneously
You can have as many player as you want !
///play 3 songs in parallel
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/song1.mp3")
);
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/song2.mp3")
);
//another way, with create, open, play & dispose the player on finish
AssetsAudioPlayer.playAndForget(
Audio("assets/audios/song3.mp3")
);
Each player has an unique generated id, you can retrieve or create them manually using
final player = AssetsAudioPlayer.withId(id: "MY_UNIQUE_ID");
๐๏ธ Playlist #
assetsAudioPlayer.open(
Playlist(
audios: [
Audio("assets/audios/song1.mp3"),
Audio("assets/audios/song2.mp3")
]
),
loopMode: LoopMode.playlist //loop the full playlist
);
assetsAudioPlayer.next();
assetsAudioPlayer.prev();
assetsAudioPlayer.playlistPlayAtIndex(1);
Audio Widget #
If you want a more flutter way to play audio, try the AudioWidget !
//inside a stateful widget
bool _play = false;
@override
Widget build(BuildContext context) {
return AudioWidget.assets(
path: "assets/audios/country.mp3",
play: _play,
child: RaisedButton(
child: Text(
_play ? "pause" : "play",
),
onPressed: () {
setState(() {
_play = !_play;
});
}
),
onReadyToPlay: (duration) {
//onReadyToPlay
},
onPositionChanged: (current, duration) {
//onPositionChanged
},
);
}
How to ๐ stop ๐ the AudioWidget ?
Just remove the Audio from the tree !
Or simply keep play: false
๐ง Listeners #
All listeners exposes Streams Using RxDart, AssetsAudioPlayer exposes some listeners as ValueObservable (Observable that provides synchronous access to the last emitted item);
๐ต Current song #
//The current playing audio, filled with the total song duration
assetsAudioPlayer.current //ValueObservable<PlayingAudio>
//Retrieve directly the current played asset
final PlayingAudio playing = assetsAudioPlayer.current.value;
//Listen to the current playing song
assetsAudioPlayer.current.listen((playingAudio){
final asset = playingAudio.assetAudio;
final songDuration = playingAudio.duration;
})
โ Current song duration #
//Listen to the current playing song
final duration = assetsAudioPlayer.current.value.duration;
โณ Current position (in seconds) #
assetsAudioPlayer.currentPosition //ValueObservable<Duration>
//retrieve directly the current song position
final Duration position = assetsAudioPlayer.currentPosition.value;
return StreamBuilder(
stream: assetsAudioPlayer.currentPosition,
builder: (context, asyncSnapshot) {
final Duration duration = asyncSnapshot.data;
return Text(duration.toString());
}),
or use a PlayerBuilder !
PlayerBuilder.currentPosition(
player: _assetsAudioPlayer,
builder: (context, duration) {
return Text(duration.toString());
}
)
or Player Builder Extension
_assetsAudioPlayer.builderCurrentPosition(
builder: (context, duration) {
return Text(duration.toString());
}
)
โถ IsPlaying #
boolean observable representing the current mediaplayer playing state
assetsAudioPlayer.isPlaying // ValueObservable<bool>
//retrieve directly the current player state
final bool playing = assetsAudioPlayer.isPlaying.value;
//will follow the AssetsAudioPlayer playing state
return StreamBuilder(
stream: assetsAudioPlayer.isPlaying,
builder: (context, asyncSnapshot) {
final bool isPlaying = asyncSnapshot.data;
return Text(isPlaying ? "Pause" : "Play");
}),
or use a PlayerBuilder !
PlayerBuilder.isPlaying(
player: _assetsAudioPlayer,
builder: (context, isPlaying) {
return Text(isPlaying ? "Pause" : "Play");
}
)
or Player Builder Extension
_assetsAudioPlayer.builderIsPlaying(
builder: (context, isPlaying) {
return Text(isPlaying ? "Pause" : "Play");
}
)
๐ Volume #
Change the volume (between 0.0 & 1.0)
assetsAudioPlayer.setVolume(0.5);
The media player can follow the system "volume mode" (vibrate, muted, normal)
Simply set the respectSilentMode optional parameter as true
_player.open(PLAYABLE, respectSilentMode: true);
https://developer.android.com/reference/android/media/AudioManager.html?hl=fr#getRingerMode()
https://developer.apple.com/documentation/avfoundation/avaudiosessioncategorysoloambient
Listen the volume
return StreamBuilder(
stream: assetsAudioPlayer.volume,
builder: (context, asyncSnapshot) {
final double volume = asyncSnapshot.data;
return Text("volume : $volume");
}),
or use a PlayerBuilder !
PlayerBuilder.volume(
player: _assetsAudioPlayer,
builder: (context, volume) {
return Text("volume : $volume");
}
)
โ Finished #
Called when the current song has finished to play,
it gives the Playing audio that just finished
assetsAudioPlayer.playlistAudioFinished //ValueObservable<Playing>
assetsAudioPlayer.playlistAudioFinished.listen((Playing playing){
})
Called when the complete playlist has finished to play
assetsAudioPlayer.playlistFinished //ValueObservable<bool>
assetsAudioPlayer.playlistFinished.listen((finished){
})
๐ Looping #
final LoopMode loopMode = assetsAudioPlayer.loop;
// possible values
// LoopMode.none : not looping
// LoopMode.single : looping a single audio
// LoopMode.playlist : looping the fyll playlist
assetsAudioPlayer.setLoopMode(LoopMode.single);
assetsAudioPlayer.loopMode.listen((loopMode){
//listen to loop
})
assetsAudioPlayer.toggleLoop(); //toggle the value of looping
๐ Play Speed #
assetsAudioPlayer.setPlaySpeed(1.5);
assetsAudioPlayer.playSpeed.listen((playSpeed){
//listen to playSpeed
})
//change play speed for a particular Audio
Audio audio = Audio.network(
url,
playSpeed: 1.5
);
assetsAudioPlayer.open(audio);
๐๏ธ Pitch #
assetsAudioPlayer.setPitch(1.2);
assetsAudioPlayer.pitch.listen((pitch){
//listen to pitch
})
//change pitch for a particular Audio
Audio audio = Audio.network(
url,
pitch: 1.2
);
assetsAudioPlayer.open(audio);
Error Handling #
By default, on playing error, it stop the audio
BUT you can add a custom behavior
_player.onErrorDo = (handler){
handler.player.stop();
};
Open another audio
_player.onErrorDo = (handler){
handler.player.open(ANOTHER_AUDIO);
};
Try to open again on same position
_player.onErrorDo = (handler){
handler.player.open(
handler.playlist.copyWith(
startIndex: handler.playlistIndex
),
seek: handler.currentPosition
);
};
Network Policies (android/iOS/macOS) #
Android only allow HTTPS calls, you will have an error if you're using HTTP,
don't forget to add INTERNET permission and seet usesCleartextTraffic="true" in your AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:usesCleartextTraffic="true"
...>
...
</application>
</manifest>
iOS only allow HTTPS calls, you will have an error if you're using HTTP,
don't forget to edit your info.plist and set NSAppTransportSecurity to NSAllowsArbitraryLoads
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
To enable http calls on macOs, you have to add input/output calls capabilities into info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
and in your
Runner/DebugProfile.entitlements
add
<key>com.apple.security.network.client</key>
<true/>
Complete Runner/DebugProfile.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
๐ถ Musics #
All musics used in the samples came from https://www.freemusicarchive.org/
๐ค Contributing #
Contributions are welcome โ bug fixes, features, docs, tests, and examples.
Workflow #
- Open an issue first for anything larger than a small fix, so we can agree on scope before you spend time on a PR.
- Fork & branch off
master. Use a descriptive branch name (fix/web-double-audio,feat/pitch-on-ios,docs/contributing). - Keep the PR focused. One concern per PR โ mixing a refactor with a bug fix makes review much harder.
- Run the checks locally before pushing:
dart format --set-exit-if-changed . flutter analyze flutter test cd example && flutter analyze - Open the PR against
master. The PR template will ask you to describe what changed, why, how you tested it, and on which platforms.
What "comprehensive testing" means here #
Because this package ships on five platforms (Android / iOS / macOS / Web / Linux / Windows where applicable), a PR is expected to:
- Add or update unit tests in
test/for any non-trivial Dart logic. - Add or update the example app (
example/) when changing a public API, so the change is runnable end-to-end. - For platform-specific changes, include a short manual test plan in the
PR description listing every platform you ran it on, with the Flutter
version (
flutter --versionoutput). - For web changes, test both DDC (
flutter run -d chrome) and WASM (flutter build web --wasm).
Review gates #
โน๏ธ Automated CI (GitHub Actions) is not wired up yet โ it will be added in a future release. Until then, the following must be run manually by the contributor before requesting review, and verified by a maintainer on merge.
Required checks (same commands CI will eventually run):
dart format --set-exit-if-changed .(formatting must match)flutter analyzeon the plugin, the web package, and the exampleflutter testexample/builds cleanly for web (JS + WASM) and Android
Review process:
- At least one approving review from a maintainer before merge.
- The CODEOWNERS file (
.github/CODEOWNERS) routes reviews to the responsible maintainer automatically.
Releases #
Releases are cut from master by a maintainer: bump the version in
pubspec.yaml, update CHANGELOG.md, tag vX.Y.Z, and publish to pub.dev.
Please do not bump the version in a PR yourself โ that's a maintainer
step so the changelog stays coherent.
Code of conduct #
Be kind. No harassment, no personal attacks. Disagree with ideas, not people. Maintainers reserve the right to close or lock threads that get heated.

