1

フラッターで音楽プレーヤーアプリを開発しています。

シナリオ: ユーザーがリストから 1 つの曲を選択すると、選択した曲が完全に再生されると、リスト内の次の曲を自動的に開始する必要があります。

現在、UI の曲の詳細とスライダー情報を更新するためにPlaybackState、ストリームを聴いています。Positionしかし、選択した曲の再生がいつ終了するかわかりません。ここでのアイデアは、選択した曲が再生されたら、リストの次の曲を再生する必要があるということです。最初に、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: また、選択した曲を終了した後も、Position ストリームが継続的に受信されます。このため、期間ラベルの更新を停止できません。

                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 に答える