Reputation: 1
I am working on an audio streaming app using the audio_service package in Flutter, and I am facing an issue where the song switching works fine in the foreground, but when the app is in the background, the song doesn't switch, and the audio player gets stuck on buffering. This issue is specific to iOS, as the app works fine on Android.
Here is what I have done so far:
I have set up background modes in the Info.plist to allow audio and Xcode also. I have implemented the necessary background task for audio playback, but song switching in the background is not functioning as expected.
This is my songHandler file
import 'dart:developer';
import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
import 'package:rxdart/rxdart.dart' as rx;
class SongHandler extends BaseAudioHandler with SeekHandler {
final AudioPlayer audioPlayer = AudioPlayer();
MediaItem? currentMediaItem;
bool hasPrevious = false;
bool hasNext = false;
void _broadcastState() {
mediaItem.add(currentMediaItem);
playbackState.add(playbackState.value.copyWith(
controls: [
if (hasPrevious) MediaControl.skipToPrevious,
if (audioPlayer.playing) MediaControl.pause else MediaControl.play,
if (hasNext) MediaControl.skipToNext,
],
systemActions: {
MediaAction.seek,
MediaAction.seekForward,
MediaAction.seekBackward,
},
processingState: {
ProcessingState.idle: AudioProcessingState.idle,
ProcessingState.loading: AudioProcessingState.loading,
ProcessingState.buffering: AudioProcessingState.buffering,
ProcessingState.ready: AudioProcessingState.ready,
ProcessingState.completed: AudioProcessingState.completed,
}[audioPlayer.processingState]!,
playing: audioPlayer.playing,
updatePosition: audioPlayer.position,
bufferedPosition: audioPlayer.bufferedPosition,
speed: audioPlayer.speed,
queueIndex: 0,
));
}
Future<void> initSong(
AudioSource audioSource,
MediaItem song,
) async {
currentMediaItem = song; // Set current media item
audioPlayer.playbackEventStream.listen((_) => _broadcastState());
await audioPlayer.setAudioSource(audioSource);
play();
}
Future<void> updateAudioSource(AudioSource audioSource, MediaItem song,
{bool? isShowButton = false}) async {
await audioPlayer.setAudioSource(audioSource);
currentMediaItem = song;
hasPrevious = isShowButton ?? false;
hasNext = isShowButton ?? false;
}
@override
Future<void> play() async {
if (currentMediaItem != null) {
await audioPlayer.play();
}
}
@override
Future<void> pause() => audioPlayer.pause();
@override
Future<void> seek(Duration position) => audioPlayer.seek(position);
@override
Future<void> skipToNext() async {
final musicPlayerViewModel = Get.find<MusicPlayerViewModel>();
musicPlayerViewModel.moveNextSong();
// Logic to skip to the next song
log('message');
}
@override
Future<void> skipToPrevious() async {
final musicPlayerViewModel = Get.find<MusicPlayerViewModel>();
musicPlayerViewModel.movePreviousSong();
// Logic to skip to the previous song
log('message');
}
Stream<PositionData> get positionDataStream =>
rx.CombineLatestStream.combine3(
audioPlayer.positionStream,
audioPlayer.bufferedPositionStream,
audioPlayer.durationStream,
(position, bufferedPosition, duration) => PositionData(
position: position,
bufferedPosition: bufferedPosition,
duration: duration ?? Duration.zero,
),
);
}
this is my listner and song switching function
void musicSetupListeners() {
playerStateSubscription?.cancel();
playerStateSubscription =
songHandler.audioPlayer.playerStateStream.listen((state) async {
log('Player state: ${state.processingState}');
switch (state.processingState) {
case ProcessingState.idle:
// Handle idle state if needed
break;
case ProcessingState.loading:
// Handle loading state if needed
break;
case ProcessingState.buffering:
// Handle buffering state if needed
break;
case ProcessingState.ready:
// Handle ready state if needed
break;
case ProcessingState.completed:
await handleCompletion();
break;
}
}, onError: (error) {
log('Stream error: $error');
});
}
Future<void> handleCompletion() async {
await songHandler.pause();
await songHandler.seek(Duration.zero);
await Future.delayed(const Duration(milliseconds: 500), () async {
try {
if (!isProcessingCompletion.value) {
isNewSongLoading.value = true;
isNewSongLoading.refresh();
isProcessingCompletion.value = true;
isProcessingCompletion.refresh();
await songHandler.stop();
await Future.delayed(const Duration(milliseconds: 500), () async {
await _playNextSong(this);
isProcessingCompletion.value = false;
});
}
} catch (e) {
log('Error during completion handling: $e');
}
});
}
Future _playNextSong(MusicPlayerViewModel musicPlayerViewModel) async {
if (isChangeList.value == false) {
if (britanyPlyController.isShuffle.value) {
await britanyPlyController.selectRandomSongs(musicPlayerViewModel);
} else {
await britanyPlyController.selectSequenceSongs(musicPlayerViewModel);
}
} else {
await selectSequenceSongs();
}
Future.delayed(
const Duration(milliseconds: 300),
() {
isNewSongLoading.value = false;
isNewSongLoading.refresh();
},
);
await songHandler.play();
}
i want any one can help me to solve out this issue.
Upvotes: -1
Views: 32