Flutter中Audio Service如何实现音频曲目选择、播放及切换?
Flutter Audio Service 实现曲目选择、播放与切换方案
1. 添加依赖
在pubspec.yaml中加入核心依赖:
dependencies: audio_service: ^0.18.11 just_audio: ^0.9.34 rxdart: ^0.27.7
2. 实现自定义AudioHandler
AudioHandler是Audio Service的核心控制层,负责对接播放器与系统音频服务:
import 'package:audio_service/audio_service.dart'; import 'package:just_audio/just_audio.dart'; import 'package:rxdart/rxdart.dart'; class AudioPlayerHandler extends BaseAudioHandler with QueueHandler, SeekHandler { final _player = AudioPlayer(); final _playlist = ConcatenatingAudioSource(children: []); AudioPlayerHandler() { // 同步播放器状态到AudioService _player.playbackEventStream.map(_transformEvent).pipe(playbackState); // 同步当前播放曲目到UI _player.currentIndexStream.listen((index) { if (index != null && index < queue.value.length) { mediaItem.add(queue.value[index]); } }); } // 转换JustAudio事件为AudioService标准状态 PlaybackState _transformEvent(PlaybackEvent event) { return PlaybackState( controls: [ MediaControl.skipToPrevious, _player.playing ? MediaControl.pause : MediaControl.play, MediaControl.skipToNext, ], systemActions: const {MediaAction.seek, MediaAction.skipToNext, MediaAction.skipToPrevious}, androidCompactActionIndices: const [0, 1, 2], processingState: { ProcessingState.idle: AudioProcessingState.idle, ProcessingState.loading: AudioProcessingState.loading, ProcessingState.buffering: AudioProcessingState.buffering, ProcessingState.ready: AudioProcessingState.ready, ProcessingState.completed: AudioProcessingState.completed, }[_player.processingState]!, playing: _player.playing, updatePosition: _player.position, bufferedPosition: _player.bufferedPosition, speed: _player.speed, queueIndex: event.currentIndex, ); } // 更新播放队列 @override Future<void> updateQueue(List<MediaItem> queue) async { await _playlist.clear(); await _playlist.addAll(queue.map((item) => AudioSource.uri(Uri.parse(item.id)))); this.queue.add(queue); } // 跳转到指定队列索引曲目并播放 @override Future<void> skipToQueueItem(int index) async { if (index < 0 || index >= queue.value.length) return; await _player.seek(Duration.zero, index: index); await play(); } // 播放控制 @override Future<void> play() => _player.play(); @override Future<void> pause() => _player.pause(); // 切换上下曲 @override Future<void> skipToPrevious() async { _player.hasPrevious ? await _player.seekToPrevious() : await _player.seek(Duration.zero); } @override Future<void> skipToNext() async { _player.hasNext ? await _player.seekToNext() : await _player.seek(Duration.zero, index: 0); } // 资源释放 Future<void> dispose() async { await _player.dispose(); await super.dispose(); } }
3. 初始化Audio Service
在App启动时完成服务初始化:
void main() async { await AudioService.init( builder: () => AudioPlayerHandler(), config: const AudioServiceConfig( androidNotificationChannelId: 'com.yourdomain.audio', androidNotificationChannelName: '音频播放器', androidNotificationOngoing: true, ), ); runApp(const MyApp()); }
4. UI层实现交互逻辑
在页面中完成曲目列表展示与播放控制:
import 'package:audio_service/audio_service.dart'; import 'package:flutter/material.dart'; class AudioPlayerPage extends StatefulWidget { const AudioPlayerPage({super.key}); @override State<AudioPlayerPage> createState() => _AudioPlayerPageState(); } class _AudioPlayerPageState extends State<AudioPlayerPage> { late AudioHandler _audioHandler; final List<MediaItem> _tracks = [ MediaItem( id: 'https://example.com/track1.mp3', title: '曲目1', artist: '艺术家A', album: '专辑X', duration: const Duration(minutes: 3, seconds: 45), ), MediaItem( id: 'https://example.com/track2.mp3', title: '曲目2', artist: '艺术家B', album: '专辑Y', duration: const Duration(minutes: 4, seconds: 12), ), MediaItem( id: 'https://example.com/track3.mp3', title: '曲目3', artist: '艺术家C', album: '专辑Z', duration: const Duration(minutes: 2, seconds: 58), ), ]; @override void initState() { super.initState(); _audioHandler = AudioService.instance; _audioHandler.updateQueue(_tracks); } @override void dispose() { _audioHandler.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('音频播放器')), body: Column( children: [ // 曲目选择列表 Expanded( child: ListView.builder( itemCount: _tracks.length, itemBuilder: (context, index) { final track = _tracks[index]; return ListTile( title: Text(track.title), subtitle: Text(track.artist!), onTap: () => _audioHandler.skipToQueueItem(index), ); }, ), ), // 播放控制栏 StreamBuilder<PlaybackState>( stream: _audioHandler.playbackState, builder: (context, snapshot) { final isPlaying = snapshot.data?.playing ?? false; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.skip_previous), iconSize: 48, onPressed: _audioHandler.skipToPrevious, ), IconButton( icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow), iconSize: 64, onPressed: isPlaying ? _audioHandler.pause : _audioHandler.play, ), IconButton( icon: const Icon(Icons.skip_next), iconSize: 48, onPressed: _audioHandler.skipToNext, ), ], ); }, ), ], ), ); } }
核心逻辑说明
- 曲目选择:点击列表项调用
skipToQueueItem(index),直接定位到指定曲目并触发播放 - 切换逻辑:
skipToPrevious()/skipToNext()自动处理首尾循环,可根据需求修改为停止播放或其他逻辑 - 状态同步:通过
StreamBuilder监听播放状态与当前曲目,实时更新UI显示
内容的提问来源于stack exchange,提问作者WD SANKA MADURANGA
相关产品推荐
相关产品推荐

