You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何用just_audio实现队列音频自定义次数循环

解决方案

一、实现指定次数循环并自动切换队列项

just_audio的LoopMode.one是无限循环,要实现指定次数循环,需要手动监听播放完成事件,计数循环次数,达到目标后切换下一曲。

1. 新增状态变量

在MyAudioHandler中添加变量跟踪循环状态:

class MyAudioHandler extends BaseAudioHandler {
  // 当前已循环次数
  int _currentLoopCount = 0;
  // 目标循环次数
  int _targetLoopCount = 1;
  // 当前音频的实际时长
  Duration? _currentAudioActualDuration;
  // 订阅当前索引变化的流
  StreamSubscription<int>? _currentIndexSubscription;

  MyAudioHandler() {
    // 监听播放完成事件
    _audioPlayer.playerStateStream.listen((state) {
      if (state.processingState == ProcessingState.completed) {
        _handleLoopCompletion();
      }
    });

    // 监听队列索引变化,重置循环状态
    _currentIndexSubscription = _audioPlayer.currentIndexStream.listen((index) {
      if (index != null && index < queue.value.length) {
        _resetLoopState(index);
      }
    });
  }
}

2. 重置循环状态与计算目标次数

添加方法处理索引切换时的循环状态重置,以及根据自定义总时长计算目标循环次数:

void _resetLoopState(int index) async {
  _currentLoopCount = 0;
  final currentItem = queue.value[index];
  
  // 从MediaItem的extras中获取自定义总播放时长
  final totalDurationMs = currentItem.extras?['totalPlayDuration'] as int?;
  if (totalDurationMs == null) {
    _targetLoopCount = 1;
    _currentAudioActualDuration = null;
    return;
  }

  // 获取音频实际时长
  _currentAudioActualDuration = await _audioPlayer.durationFuture;
  if (_currentAudioActualDuration == null) {
    _targetLoopCount = 1;
    return;
  }

  // 计算目标循环次数(总时长 ÷ 音频实际时长)
  _targetLoopCount = totalDurationMs ~/ _currentAudioActualDuration!.inMilliseconds;
  // 若需处理总时长非整数倍的情况,可取消下方注释调整逻辑
  // if (totalDurationMs % _currentAudioActualDuration!.inMilliseconds != 0) {
  //   _targetLoopCount += 1;
  // }
}

3. 处理循环完成逻辑

添加方法判断是否达到循环次数,决定继续循环或切换下一曲:

Future<void> _handleLoopCompletion() async {
  _currentLoopCount++;
  if (_currentLoopCount >= _targetLoopCount) {
    // 达到目标次数,切换至下一曲
    await skipToNext();
  } else {
    // 未达次数,重新播放当前曲
    await _audioPlayer.seek(Duration.zero);
    await _audioPlayer.play();
  }
}

4. 修改播放与队列添加逻辑

取消无限循环设置,改用手动控制:

@override
Future<void> play() async {
  setRepeatMode(AudioServiceRepeatMode.none);
  _audioPlayer.setLoopMode(LoopMode.none);
  _audioPlayer.play();
}

@override
Future<void> addQueueItems(List<MediaItem> mediaItems) async {
  final audioSource = mediaItems.map(_createAudioSource);
  _playlist.addAll(audioSource.toList());
  final newQueue = queue.value..addAll(mediaItems);
  queue.add(newQueue);
  await _audioPlayer.setAudioSource(_playlist);
  setRepeatMode(AudioServiceRepeatMode.none);
  _audioPlayer.setLoopMode(LoopMode.none);
}

二、让自定义时长生效

just_audio会自动读取音频文件的实际时长,因此不能直接用MediaItem.duration存储总播放时长,改用extras字段存储,再在AudioHandler中更新显示的时长:

1. 修改MediaItem创建方式

将自定义总时长存入extras:

ElevatedButton(
  onPressed: () async {
    List<MediaItem> mediaItems = [
      MediaItem(
        id: 'assets/audio/jingle.mp3',
        title: 'jingle',
        // 存储总播放时长(15秒)
        extras: {'totalPlayDuration': Duration(seconds: 15).inMilliseconds},
      ),
      MediaItem(
        id: 'assets/audio/jazz.mp3',
        title: 'jazz',
        // 存储总播放时长(60秒)
        extras: {'totalPlayDuration': Duration(seconds: 60).inMilliseconds},
      ),
    ];
    getIt<AudioHandler>().addQueueItems(mediaItems);
  },
  child: const Text('Add to queue'),
),

2. 更新MediaItem显示时长

在索引变化时,将自定义总时长设置为MediaItem的显示时长:

// 在MyAudioHandler的初始化监听中添加
_currentIndexSubscription = _audioPlayer.currentIndexStream.listen((index) {
  if (index != null && index < queue.value.length) {
    final item = queue.value[index];
    final totalDurationMs = item.extras?['totalPlayDuration'] as int?;
    // 用自定义总时长覆盖显示的duration
    final displayDuration = totalDurationMs != null 
        ? Duration(milliseconds: totalDurationMs) 
        : item.duration;
    setMediaItem(item.copyWith(duration: displayDuration));
  }
});

三、清理资源

记得在AudioHandler销毁时取消订阅:

@override
Future<void> dispose() async {
  _currentIndexSubscription?.cancel();
  await _audioPlayer.dispose();
  await super.dispose();
}

内容的提问来源于stack exchange,提问作者Kdon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 07:46:56