Reputation: 894
I created player audio and I used the just_audio plugin, I have a problem when the audio player completes and does not return to the beginning. I tried to use player.playerStateStream.listen()
but when I click on the play icon it does not play the audio and the start duration does not change to 0, but when I manually move the slider it works, how can this problem be solved?
Edit:
What am I need?
I need after audio complete the slider back to the beginning and reset duration just like the first time I played the audio and pause the audio with onPressed
play audio.
Code:
String currentTime = "", endTime = "";
double minDuration = 0, maxDuration = 0, currentDuration = 0;
void setAudio(String url) async {
setState(() => isLoadAudio = true);
await player.setUrl(url);
currentDuration = minDuration;
maxDuration = player.duration!.inMilliseconds.toDouble();
setState(() {
currentTime = getDuration(currentDuration);
endTime = getDuration(maxDuration);
});
isPlaying = false;
changeStatusPlaying();
player.positionStream.listen((duration) {
currentDuration = duration.inMilliseconds.toDouble();
setState(() => currentTime = getDuration(currentDuration));
});
setState(() => isLoadAudio = false);
player.playerStateStream.listen((state) {
if (state.processingState == ProcessingState.completed) {
setState(() {
currentDuration = minDuration;
if (isRepeating == true) {
isPlaying = true;
} else {
isPlaying = false;
isRepeating = false;
}
});
}
});
}
void changeStatusPlaying() {
setState(() => isPlaying = !isPlaying);
isPlaying ? player.play() : player.pause();
currentDuration == maxDuration ? isPlaying : !isPlaying;
}
String getDuration(double value) {
Duration duration = Duration(milliseconds: value.round());
return [duration.inHours, duration.inMinutes, duration.inSeconds]
.map((e) => e.remainder(60).toString().padLeft(2, "0"))
.join(":");
}
Slider(
value: currentDuration,
min: minDuration,
max: maxDuration,
onChanged: (v) {
currentDuration = v;
playAudio(currentDuration);
},
),
Upvotes: 4
Views: 7578
Reputation: 103
For those running into the "Cannot fire new event. Controller is already firing an event" exception, when asking the player to pause in the player completion event handler: I've tried to use a dear old (dirty) trick from JS programming and it seems to be working.
Let the audio player state machine end the completed
event notification process before asking the player to change its state (with .pause()
, .seek()
, ...). This can be accomplished by deferring the calls to the "next event loop iteration", by using a Future
(a Promise
in JS world).
_audioPlayer.playbackEventStream.listen((event) async {
if (event.processingState == just_audio.ProcessingState.completed) {
if (_audioPlayer.playing) {
/// Deferring "pause" and "seek" calls to the next tick is needed because of the "sync: true" in just_audio code:
/// ```
/// final _playbackEventSubject = BehaviorSubject<PlaybackEvent>(sync: true);
/// ```
Future.delayed(Duration.zero, () async {
await _audioPlayer.pause();
await _audioPlayer.seek(Duration.zero);
});
}
}
});
Upvotes: 0
Reputation: 49
I solved this by pausing the audio once the processingState is completed, then player.seek(Duration.zero);
StreamBuilder<PlayerState>(
stream: player.playerStateStream,
builder: (context, snapshot) {
if (snapshot.data?.processingState ==
ProcessingState.completed) {
player.pause();
player.seek(Duration.zero);
}
return Icon(
snapshot.data?.playing == true
? Icons.pause
: Icons.play_arrow,
);
}),
Upvotes: 1
Reputation: 51
Try this:
bool _isPlaying = false;
bool _isCompleted = false;
Duration? _duration = Duration.zero;
Duration? _position = Duration.zero;
After player has completed, move position to Duration(seconds: 0)
Init state
@override
void initState() {
_initLoadFile();
// Listen player states
_audioPlayer.playerStateStream.listen((event) async {
setState(() {
_isPlaying = event.playing;
});
if (event.processingState == ProcessingState.completed) {
setState(() {
_isPlaying = false;
_position = const Duration(seconds: 0);
_isCompleted = true;
});
}
});
// Listen audio duration
_audioPlayer.durationStream.listen((newDuration) {
setState(() {
_duration = newDuration;
});
});
// Listen audio position
_audioPlayer.positionStream.listen((newPosition) {
setState(() {
_position = newPosition;
});
});
super.initState();
}
In widget
ElevatedButton(
onPressed: () async {
if (!_isPlaying) {
setState(() {
_isPlaying = true;
_isCompleted = false;
});
await _audioPlayer.play();
if (_isCompleted) {
await _audioPlayer.seek(const Duration(seconds: 0));
await _audioPlayer.pause();
}
}
if (_isPlaying) {
setState(() {
_isPlaying = false;
});
await _audioPlayer.pause();
}
},
child: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
),
),
Upvotes: 4
Reputation: 36323
Set the audio player to loop:
player.setLoopMode(LoopMode.one);
Full example (put your audio file at /assets/audio):
class MySimplePlayer extends StatefulWidget {
final String name, asset;
const MySimplePlayer(this.name, String filename)
: this.asset = "assets/audio/$filename";
@override
_MySimplePlayerState createState() => _MySimplePlayerState();
}
class _MySimplePlayerState extends State<MySimplePlayer> {
late AudioPlayer player;
bool _ready = false;
@override
void initState() {
super.initState();
player = AudioPlayer();
player.setLoopMode(LoopMode.one);
player.setAsset(widget.asset).then((_) {
if (mounted) setState(() => _ready = true);
});
}
@override
void dispose() {
player.stop();
player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!_ready) return const SizedBox();
return Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const SizedBox(width: 16),
Expanded(child: Text(widget.name)),
if (!player.playing)
IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () async {
player.play();
setState(() {});
},
),
if (player.playing)
IconButton(
icon: Icon(Icons.pause),
onPressed: () async {
await player.pause();
setState(() {});
},
),
IconButton(
icon: Icon(Icons.stop),
onPressed: () async {
await player.stop();
setState(() {});
},
),
const SizedBox(width: 16),
],
),
);
}
}
Upvotes: 0