Skip to content

Play simultaneously music/audio from assets/network/file directly from Flutter, compatible with android / ios / web / macos, displays notifications

License

Notifications You must be signed in to change notification settings

florent37/Flutter-AssetsAudioPlayer

Repository files navigation

🎧 assets_audio_player 🔊

pub package Awesome Flutter

Codemagic build status CodeFactor

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,
);

sample1 sample1

📥 Import

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:)

Buy Me a Coffee at ko-fi.com

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.

  1. Create an audio directory in your assets (not necessary named "audios" )
  2. Declare it inside your pubspec.yaml
flutter:
assets:
-assets/audios/

🛠️ Getting Started

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();

Notifications

notification

notification

on iOS, it will useMPNowPlayingInfoCenter

  1. 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
),
);
  1. open withshowNotification: true
_player.open(audio, showNotification:true)

Custom notification

Custom icon (android only)

By ResourceName

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"/>

By Manifest

  1. Add your icon into your android'sresfolder (android/app/src/main/res)

  2. 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
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
});

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-permissionandroid: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 generatedid,you can retrieve or create them manually using

finalplayer=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 theAudioWidget!

sample

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

🎧 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
finalPlayingAudioplaying=assetsAudioPlayer.current.value;

//Listen to the current playing song
assetsAudioPlayer.current.listen((playingAudio){
finalasset=playingAudio.assetAudio;
finalsongDuration=playingAudio.duration;
})

⌛ Current song duration

//Listen to the current playing song
finalduration=assetsAudioPlayer.current.value.duration;

⏳ Current position (in seconds)

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());
}
)

▶ IsPlaying

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");
}
)

🔊 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 therespectSilentModeoptional 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");
}
)

✋ 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((Playingplaying){

})

Called when the complete playlist has finished to play

assetsAudioPlayer.playlistFinished//ValueObservable<bool>

assetsAudioPlayer.playlistFinished.listen((finished){

})

🔁 Looping

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

🏃 Play Speed

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);

🎙️ Pitch

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);

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

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

🎶 Musics

All musics used in the samples came fromhttps:// freemusicarchive.org/