使用audio_service与just_audio时通知为空且无法正常关闭问题
问题
使用audio_service和just_audio实现后台音频播放时遇到两个问题:
- 后台通知仅显示图标,无任何播放控制组件(播放/暂停/停止等按钮)
- 应用完全退出后,通知仍在播放音频,且无法像普通通知一样滑动删除
相关代码
void main() async{ WidgetsFlutterBinding.ensureInitialized(); //iniciaStream(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); audioHandler = await AudioService.init( builder: () => AudioPlayerTask(), config: const AudioServiceConfig( androidNotificationChannelId: 'meuapp.channel.audio', androidNotificationChannelName: 'meu app', androidNotificationOngoing: false, androidStopForegroundOnPause: false, notificationColor: Color.fromARGB(10,255, 209, 0), ), ); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); runApp( const MaterialApp( home: Principal(),) ); } class AudioPlayerTask extends BaseAudioHandler with SeekHandler { static final _item = MediaItem( id: urlList[currentSetStream], album: "Science Friday", title: "titulo", artist: "artista", displayTitle: "Nome da rádio",//radiosList[currentSetStream], duration: const Duration(milliseconds: 5739820), //artUri: Image.asset(imgList[currentSetStream]), ); final AudioPlayer _player = AudioPlayer(); AudioPlayerHandler() { _player.playbackEventStream.map(_transformEvent).pipe(playbackState); mediaItem.add(_item); _player.setAudioSource(AudioSource.uri(Uri.parse(_item.id))); } @override Future<void> play() async{ await _player.setUrl(urlList[currentSetStream]); await _player.play(); playbackState.add( playbackState.value.copyWith( playing: true, processingState: AudioProcessingState.ready, ), ); PlaybackState( controls: [MediaControl.pause, MediaControl.stop], processingState: AudioProcessingState.ready, playing: true, bufferedPosition: _player.bufferedPosition, speed: _player.speed, ); } @override Future<void> setVolume(double volume) async { await _player.setVolume(volume); } @override Future<void> pause() async{ _player.pause(); } @override Future<void> seek(Duration position) => _player.seek(position); @override Future<void> stop() => _player.stop(); PlaybackState _transformEvent(PlaybackEvent event) { return PlaybackState( controls: [ MediaControl.rewind, if (_player.playing) MediaControl.pause else MediaControl.play, MediaControl.stop, MediaControl.fastForward, ], systemActions: const { MediaAction.seek, MediaAction.seekForward, MediaAction.seekBackward, }, androidCompactActionIndices: const [0, 1, 3], processingState: const { 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, queueIndex: event.currentIndex, ); } PlaybackState updatePlaybackState(PlaybackEvent event) { return PlaybackState( controls: [ MediaControl.rewind, if (_player.playing) MediaControl.pause else MediaControl.play, MediaControl.stop, MediaControl.fastForward, ], systemActions: const { MediaAction.seek, MediaAction.seekForward, MediaAction.seekBackward, }, androidCompactActionIndices: const [0, 1, 3], processingState: const { 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, queueIndex: event.currentIndex, ); } }
问题修复方案
1. 解决通知无控制组件问题
核心问题是状态流未正确绑定,导致通知接收不到控制按钮配置:
- 修正构造函数名:原代码中构造函数
AudioPlayerHandler()与类名AudioPlayerTask不匹配,导致初始化逻辑完全没执行。将构造函数改为AudioPlayerTask(),确保_player.playbackEventStream与playbackState流绑定。 - 移除play方法中的无效代码:play方法最后新建的
PlaybackState未添加到流中,属于无效代码;同时copyWith未保留controls配置,直接删除这部分,依赖_transformEvent生成带控制按钮的状态。 - 避免重复设置音频源:初始化时已调用
setAudioSource,play方法只需直接调用_player.play()即可。
2. 解决应用退出后音频持续播放、通知无法删除问题
需要在停止播放时正确终止音频服务:
- 完善stop方法:不仅停止播放器,还要更新状态为idle、停止前台服务并关闭音频服务:
@override Future<void> stop() async { await _player.stop(); await playbackState.add(playbackState.value.copyWith( processingState: AudioProcessingState.idle, playing: false, )); await stopForeground(true); // 停止前台服务并移除通知 await close(); // 关闭音频服务 } - 监听应用生命周期:在主页面添加
AppLifecycleListener,当应用进入后台或被销毁时,调用audioHandler.stop()确保服务终止。
修复后的关键代码片段
class AudioPlayerTask extends BaseAudioHandler with SeekHandler { static final _item = MediaItem( id: urlList[currentSetStream], album: "Science Friday", title: "titulo", artist: "artista", displayTitle: "Nome da rádio", duration: const Duration(milliseconds: 5739820), ); final AudioPlayer _player = AudioPlayer(); // 修正构造函数名 AudioPlayerTask() { _player.playbackEventStream.map(_transformEvent).pipe(playbackState); mediaItem.add(_item); _player.setAudioSource(AudioSource.uri(Uri.parse(_item.id))); } @override Future<void> play() async{ await _player.play(); } @override Future<void> pause() async{ await _player.pause(); } @override Future<void> stop() async { await _player.stop(); await playbackState.add(playbackState.value.copyWith( processingState: AudioProcessingState.idle, playing: false, )); await stopForeground(true); await close(); } PlaybackState _transformEvent(PlaybackEvent event) { return PlaybackState( controls: [ MediaControl.rewind, if (_player.playing) MediaControl.pause else MediaControl.play, MediaControl.stop, MediaControl.fastForward, ], systemActions: const { MediaAction.seek, MediaAction.seekForward, MediaAction.seekBackward, }, androidCompactActionIndices: const [0, 1, 3], processingState: const { 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, queueIndex: event.currentIndex, ); } }
额外注意事项
- 测试前卸载旧版本应用,避免通知缓存问题
- 确保
urlList[currentSetStream]为有效音频流地址,避免初始化加载失败 - 可在主页面添加生命周期监听,确保应用退出时终止音频服务
内容的提问来源于stack exchange,提问作者Raul Tomé
相关产品推荐
相关产品推荐

