I can't fully control settings in audio_services.
When I only have MediaControl.pause
, it only displays the pause button. But as soon as I add MediaControl.skipToPrevious
and MediaControl.skipToNext
they appear regardless of androidCompactActionIndices
. When adding additional buttons such as MediaControl.stop
they do not appear.
This is my code of AudioHandler
:
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import '../notifiers/play_button_notifier.dart';
import '../notifiers/progress_notifier.dart';
import '../notifiers/repeat_button_notifier.dart';
Future<AudioHandler> initAudioService() async {
return await AudioService.init(
builder: () => MyAudioHandler(),
config: const AudioServiceConfig(
androidNotificationChannelId: 'com.my_comp.music_app.audio',
androidNotificationChannelName: 'Music app',
androidNotificationOngoing: true,
androidStopForegroundOnPause: true,
),
);
}
AudioSource AS = AudioSource.uri(
Uri.parse(
'link to image'),
tag: MediaItem(
id: "1000000",
title: "Song Name",
artist: "Author name",
album: "Album name",
artUri: Uri.parse(
"link to source"),
),
);
class MyAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler {
final _player = AudioPlayer();
final _playlist = ConcatenatingAudioSource(children: [AS]);
MyAudioHandler() {
print("Audio handler inited");
_loadEmptyPlaylist();
_notifyAudioHandlerAboutPlaybackEvents();
_listenForDurationChanges();
_listenForCurrentSongIndexChanges();
_listenForSequenceStateChanges();
}
Future<void> _loadEmptyPlaylist() async {
try {
await _player.setAudioSource(_playlist);
} catch (e) {
print("Error: $e");
}
}
@override
Future<void> play() async {
_player.play();
}
@override
Future<void> pause() async {
_player.pause();
}
@override
Future<void> stop() async {
_player.stop();
}
@override
Future<void> seek(Duration position) async {
false ? _player.seek(_player.position) : _player.seek(position);
}
@override
Future<void> customAction(String name, [Map<String, dynamic>? extras]) async {
print(name);
if (name == 'dispose') {
await _player.dispose();
super.stop();
}
}
@override
Future<void> skipToNext() async {
playbackState.add(
playbackState.value.copyWith(
controls: [
MediaControl.play,
MediaControl.stop,
MediaControl.skipToNext,
MediaControl.skipToPrevious
],
androidCompactActionIndices: const [0, 3, 2],
),
);
}
@override
Future<void> skipToQueueItem(int i) async {
print(i);
}
final currentSongTitleNotifier = ValueNotifier<String>('');
final playlistNotifier = ValueNotifier<List<String>>([]);
final progressNotifier = ProgressNotifier();
final repeatButtonNotifier = RepeatButtonNotifier();
final isFirstSongNotifier = ValueNotifier<bool>(true);
final playButtonNotifier = PlayButtonNotifier();
final isLastSongNotifier = ValueNotifier<bool>(true);
final isShuffleModeEnabledNotifier = ValueNotifier<bool>(false);
void _notifyAudioHandlerAboutPlaybackEvents() {
_player.playbackEventStream.listen((PlaybackEvent event) {
final playing = _player.playing;
playbackState.add(playbackState.value.copyWith(
controls: [
MediaControl.skipToPrevious,
MediaControl.pause,
MediaControl.skipToNext,
],
/*controls: [
MediaControl.skipToPrevious,
if (playing) MediaControl.pause else MediaControl.play,
MediaControl.stop,
MediaControl.skipToNext,
],*/
systemActions: const {
MediaAction.seek,
},
androidCompactActionIndices: const [1, 2],
processingState: const {
ProcessingState.idle: AudioProcessingState.idle,
ProcessingState.loading: AudioProcessingState.loading,
ProcessingState.buffering: AudioProcessingState.buffering,
ProcessingState.ready: AudioProcessingState.ready,
ProcessingState.completed: AudioProcessingState.completed,
}[_player.processingState]!,
repeatMode: const {
LoopMode.off: AudioServiceRepeatMode.none,
LoopMode.one: AudioServiceRepeatMode.one,
LoopMode.all: AudioServiceRepeatMode.all,
}[_player.loopMode]!,
shuffleMode: (_player.shuffleModeEnabled)
? AudioServiceShuffleMode.all
: AudioServiceShuffleMode.none,
playing: playing,
updatePosition: _player.position,
bufferedPosition: _player.bufferedPosition,
speed: _player.speed,
queueIndex: event.currentIndex,
));
});
}
void _listenForDurationChanges() {
_player.durationStream.listen((duration) {
var index = _player.currentIndex;
final newQueue = queue.value;
if (index == null || newQueue.isEmpty) return;
if (_player.shuffleModeEnabled) {
index = _player.shuffleIndices!.indexOf(index);
}
final oldMediaItem = newQueue[index];
final newMediaItem = oldMediaItem.copyWith(duration: duration);
newQueue[index] = newMediaItem;
queue.add(newQueue);
mediaItem.add(newMediaItem);
});
}
void _listenForCurrentSongIndexChanges() {
_player.currentIndexStream.listen((index) {
final playlist = queue.value;
if (index == null || playlist.isEmpty) return;
if (_player.shuffleModeEnabled) {
index = _player.shuffleIndices!.indexOf(index);
}
mediaItem.add(playlist[index]);
});
}
void _listenForSequenceStateChanges() {
_player.sequenceStateStream.listen((SequenceState? sequenceState) {
final sequence = sequenceState?.effectiveSequence;
if (sequence == null || sequence.isEmpty) return;
final items = sequence.map((source) => source.tag as MediaItem);
queue.add(items.toList());
});
}
}