1

我在颤振中开发了一个音乐播放器应用程序。

场景:当用户从列表中选择一首歌曲时,一旦选择的歌曲播放完毕,自动需要开始列表中的下一首歌曲。

目前我正在收听流媒体以更新 UI 上的歌曲详细信息和滑块信息PlaybackStatePosition但是,我无法确定所选歌曲何时播放完毕。这里的想法是,一旦播放了选定的歌曲,就必须播放列表中的下一首歌曲。最初,AudioProcessingState 从连接状态变为就绪状态。第一个媒体项目已成功播放。但,

问题 1:即使在完全播放完第一个媒体项后,AudioProcessingState 仍然存在ready(未更改为已完成)。就连演奏也true总是如此。下面是播放和暂停 UI 的逻辑。

      StreamBuilder<PlaybackState>(
                      stream: AudioService.playbackStateStream,
                      builder: (context, snapshot) {
                        final processingState = snapshot.data?.processingState;
                        print('Current Processingstate: $processingState');
                        final playing = snapshot.data?.playing ?? false;

                        if (playing)
                          return IconButton(
                              iconSize: 40,
                              onPressed: () {
                                AudioService.pause();
                              },
                              icon: Icon(FontAwesomeIcons.pause,
                                  color: Colors.white));
                        else
                          return IconButton(
                              iconSize: 40,
                              onPressed: () {
                                if (AudioService.running) {
                                  AudioService.play();
                                } else {
                                  List<dynamic> list = [];

                                  for (int i = 0;
                                      i < this.widget.mediaList.length;
                                      i++) {
                                    var m = this.widget.mediaList[i].toJson();
                                    list.add(m);
                                  }
                                  var params = {
                                    "data": list,
                                    'index': this.widget.currentIndex
                                  };

                                  AudioService.connect();
                                  AudioService.start(
                                      backgroundTaskEntrypoint:
                                          _backgroundTaskEntrypoint,
                                      params: params);
                                }
                              },
                              icon: Icon(FontAwesomeIcons.play,
                                  color: Colors.white));
                      },
                    ),

问题 2:此外,即使在完成所选歌曲后,位置流也会持续接收。因此,我无法停止更新持续时间标签。

                StreamBuilder<Duration>(
                      stream: AudioService.positionStream,
                      builder: (context, snapshot) {
                        final mediaState = snapshot.data;
                        return SeekBar(
                          duration: this.widget.mediaList[current].duration ??
                              Duration.zero,
                          position: aPosition,
                          onChangeEnd: (newPosition) {
                            AudioService.seekTo(newPosition);
                          },
                        );
                      },
                    ),

简单来说,

  1. 我怎样才能知道所选歌曲已播放完毕?
  2. 歌曲播放完成后如何更新滑块?
4

1 回答 1

1

有一个未完成的功能请求添加一种新类型的事件订阅,它可以在播放器自动前进到此处序列中的下一个项目时通知您。您可以通过单击“竖起大拇指”按钮为该功能投票。

但是,此评论中还描述了一种解决方法。您可以做的是听player.currentIndexStream哪个会告诉您当前项目何时更改。当玩家自动前进到下一个项目时,这个索引会改变,但你不能假设每次当前索引改变都是因为自动前进,因为它也可能是因为手动搜索. 出于这个原因,解决方法是只保留一个布尔变量,指示是否使用了手动搜索,以便您知道是什么情况。每次搜索时,都将该布尔变量设置为 true,并在搜索完成后将其设置回 false。

为了使它更容易,您可以制作自己版本的 seek 方法来执行此操作,例如mySeek,甚至将其作为扩展方法添加到AudioPlayer

bool _seeking = false;
extension AudioPlayerExtension on AudioPlayer {
  Future<void> mySeek(Duration position, {int? index}) async {
    _seeking = true;
    await seek(position);
    _seeking = false;
  }
}

然后解决方案的另一部分是听currentIndexStream

_player.currentIndexStream.listen((index) {
  if (index == null) return;
  if (_seeking) {
    // the index changed due to a manual seek
  } else {
    // the index changed due to an auto-advance
    onAutoAdvance();
  }
});

所以现在,每次你想手动寻找不同的项目时,你使用:

player.mySeek(pos, index: i);

然后要处理由于自动前进而导致项目更改的情况,请在此处编写代码来处理它:

void onAutoAdvance() {
}
于 2021-08-10T13:13:01.497 回答