我创建了播放器音频并使用了 just_audio 插件,当音频播放器完成并且不返回到开头时我遇到问题。我尝试使用
player.playerStateStream.listen()
,但是当我点击播放图标时,它不播放音频,并且开始持续时间也不会更改为0,但是当我手动移动滑块时,它可以工作,如何解决这个问题?
编辑:
我需要什么?
我需要在音频完成后将滑块返回到开头并重置持续时间,就像我第一次播放音频一样,并使用
onPressed
播放音频暂停音频。
代码:
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);
},
),
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,
),
),
将音频播放器设置为循环播放:
player.setLoopMode(LoopMode.one);
完整示例(将音频文件放在 /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),
],
),
);
}
}
我通过在processingState完成后暂停音频来解决这个问题,然后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,
);
}),
我尝试使用 JS 编程中的一个古老(肮脏)技巧,它似乎有效。
让音频播放器状态机结束
completed
事件通知过程,然后再要求播放器更改其状态(使用 .pause()
、.seek()
,...)。这可以通过使用 Future
(JS 世界中的 Promise
)推迟对“下一个事件循环迭代”的调用来完成。
_audioPlayer.playbackEventStream.listen((event) async {
if (event.processingState == just_audio.ProcessingState.completed) {
Future.delayed(Duration.zero, () async {
await _audioPlayer.pause();
await _audioPlayer.seek(Duration.zero);
});
}
});