Play music/audio stored in assets files (simultaneously) directly from Flutter (android / ios / web / macos).
You can also use play audio files fromnetworkusing their url,radios/livestreamandlocal 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,
);
dependencies:
assets_audio_player:^3.0.8
Works withflutter: ">=3.3.0"
,be sure to upgrade your sdk
You like the package? buy me a kofi:)
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 | ✅ | 🚫 | 🚫 | 🚫 |
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/
finalassetsAudioPlayer=AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio("assets/audios/song1.mp3"),
);
You can also playnetwork songsfromurl
finalassetsAudioPlayer=AssetsAudioPlayer();
try{
awaitassetsAudioPlayer.open(
Audio.network("http:// mysite /myMp3file.mp3"),
);
}catch(t) {
//mp3 unreachable
}
LiveStream / Radiofromurl
The main difference with network, if you pause/play, on livestream it will resume to present duration
finalassetsAudioPlayer=AssetsAudioPlayer();
try{
awaitassetsAudioPlayer.open(
Audio.liveStream(MY_LIVESTREAM_URL),
);
}catch(t) {
//stream unreachable
}
And playsongs from file
//create a new player
finalassetsAudioPlayer=AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio.file(FILE_URI),
);
for file uri, please look athttps://pub.dev/packages/path_provider
assetsAudioPlayer.playOrPause();
assetsAudioPlayer.play();
assetsAudioPlayer.pause();
assetsAudioPlayer.seek(Durationto);
assetsAudioPlayer.seekBy(Durationby);
assetsAudioPlayer.forwardRewind(doublespeed);
//if positive, forward, if negative, rewind
assetsAudioPlayer.stop();
on iOS, it will useMPNowPlayingInfoCenter
- Add metas inside your audio
finalaudio=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 icon (android only)
Make sure you added those icons inside yourandroid/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 /a/android /tools/tech-docs/new-build-system/resource-shrinking
<?xmlversion="1.0"encoding="utf-8"?>
<resourcesxmlns:tools="http://schemas.android /tools"
tools:keep="@drawable/ic_next_custom, @drawable/ic_prev_custom, @drawable/ic_pause_custom, @drawable/ic_play_custom, @drawable/ic_stop_custom"/>
-
Add your icon into your android's
res
folder (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" />
Add in main
AssetsAudioPlayer.setupNotificationsOpenAction((notification) {
//custom action
returntrue;//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
returnfalse;//true: handled, does not notify others listeners
//false: enable others listeners to handle it
});
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");
}
)
)
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
),
);
You have to enable notification to make them work
Available remote commands:
- Play / Pause
- Next
- Prev
- Stop
(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-permissionandroid:name="android.permission.BLUETOOTH"/>
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 generatedid
,you can retrieve or create them manually using
finalplayer=AssetsAudioPlayer.withId(id:"MY_UNIQUE_ID");
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);
If you want a more flutter way to play audio, try theAudioWidget
!
//inside a stateful widget
bool_play=false;
@override
Widgetbuild(BuildContextcontext) {
returnAudioWidget.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 keepplay: false
All listeners exposes Streams Using RxDart, AssetsAudioPlayer exposes some listeners as ValueObservable (Observable that provides synchronous access to the last emitted item);
//The current playing audio, filled with the total song duration
assetsAudioPlayer.current//ValueObservable<PlayingAudio>
//Retrieve directly the current played asset
finalPlayingAudioplaying=assetsAudioPlayer.current.value;
//Listen to the current playing song
assetsAudioPlayer.current.listen((playingAudio){
finalasset=playingAudio.assetAudio;
finalsongDuration=playingAudio.duration;
})
//Listen to the current playing song
finalduration=assetsAudioPlayer.current.value.duration;
assetsAudioPlayer.currentPosition//ValueObservable<Duration>
//retrieve directly the current song position
finalDurationposition=assetsAudioPlayer.currentPosition.value;
returnStreamBuilder(
stream:assetsAudioPlayer.currentPosition,
builder:(context, asyncSnapshot) {
finalDurationduration=asyncSnapshot.data;
returnText(duration.toString());
}),
or use a PlayerBuilder!
PlayerBuilder.currentPosition(
player:_assetsAudioPlayer,
builder:(context, duration) {
returnText(duration.toString());
}
)
or Player Builder Extension
_assetsAudioPlayer.builderCurrentPosition(
builder:(context, duration) {
returnText(duration.toString());
}
)
boolean observable representing the current mediaplayer playing state
assetsAudioPlayer.isPlaying// ValueObservable<bool>
//retrieve directly the current player state
finalboolplaying=assetsAudioPlayer.isPlaying.value;
//will follow the AssetsAudioPlayer playing state
returnStreamBuilder(
stream:assetsAudioPlayer.isPlaying,
builder:(context, asyncSnapshot) {
finalboolisPlaying=asyncSnapshot.data;
returnText(isPlaying?"Pause":"Play");
}),
or use a PlayerBuilder!
PlayerBuilder.isPlaying(
player:_assetsAudioPlayer,
builder:(context, isPlaying) {
returnText(isPlaying?"Pause":"Play");
}
)
or Player Builder Extension
_assetsAudioPlayer.builderIsPlaying(
builder:(context, isPlaying) {
returnText(isPlaying?"Pause":"Play");
}
)
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 therespectSilentMode
optional parameter astrue
_player.open(PLAYABLE,respectSilentMode:true);
https://developer.android /reference/android/media/AudioManager.html?hl=fr#getRingerMode()
https://developer.apple /documentation/avfoundation/avaudiosessioncategorysoloambient
Listen the volume
returnStreamBuilder(
stream:assetsAudioPlayer.volume,
builder:(context, asyncSnapshot) {
finaldoublevolume=asyncSnapshot.data;
returnText("volume: $volume");
}),
or use a PlayerBuilder!
PlayerBuilder.volume(
player:_assetsAudioPlayer,
builder:(context, volume) {
returnText("volume: $volume");
}
)
Called when the current song has finished to play,
it gives the Playing audio that just finished
assetsAudioPlayer.playlistAudioFinished//ValueObservable<Playing>
assetsAudioPlayer.playlistAudioFinished.listen((Playingplaying){
})
Called when the complete playlist has finished to play
assetsAudioPlayer.playlistFinished//ValueObservable<bool>
assetsAudioPlayer.playlistFinished.listen((finished){
})
finalLoopModeloopMode=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
assetsAudioPlayer.setPlaySpeed(1.5);
assetsAudioPlayer.playSpeed.listen((playSpeed){
//listen to playSpeed
})
//change play speed for a particular Audio
Audioaudio=Audio.network(
url,
playSpeed:1.5
);
assetsAudioPlayer.open(audio);
assetsAudioPlayer.setPitch(1.2);
assetsAudioPlayer.pitch.listen((pitch){
//listen to pitch
})
//change pitch for a particular Audio
Audioaudio=Audio.network(
url,
pitch:1.2
);
assetsAudioPlayer.open(audio);
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
);
};
Android only allow HTTPS calls, you will have an error if you're using HTTP,
don't forget to add INTERNET permission and seetusesCleartextTraffic= "true"
in yourAndroidManifest.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 yourinfo.plistand setNSAppTransportSecurity
toNSAllowsArbitraryLoads
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
To enable http calls on macOs, you have to add input/output calls capabilities intoinfo.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/>
CompleteRunner/DebugProfile.entitlements
<?xml version= "1.0" encoding= "UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http:// apple /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>
All musics used in the samples came fromhttps:// freemusicarchive.org/