Flutter本地通知自定义音效下拉通知栏后停止,如何让音效持续播放?
解决Flutter本地通知下拉栏后音效停止的问题
问题根源是通知自带的sound属性设计用于播放短提示音,当用户下拉通知栏交互时,系统会自动终止通知的音频播放。要实现持续播放,必须脱离通知的音频机制,改用独立的后台音频服务。
具体解决方案
1. 引入必要依赖
在pubspec.yaml中添加后台音频相关插件:
dependencies: flutter_local_notifications: ^16.1.0 # 替换为最新版本 audio_service: ^0.18.11 # 处理后台音频播放 just_audio: ^0.9.34 # 音频播放核心 timezone: ^0.9.2
2. 实现后台音频服务
创建音频服务处理类,确保音频在后台持续播放:
import 'package:audio_service/audio_service.dart'; import 'package:just_audio/just_audio.dart'; class AdhanAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler { final _player = AudioPlayer(); AdhanAudioHandler() { // 将播放器状态同步到音频服务 _player.playbackEventStream.map(_convertToPlaybackState).pipe(playbackState); } // 播放指定音效 Future<void> playAdhan(String soundAssetPath) async { await _player.setAsset('assets/$soundAssetPath'); // 假设音效放在assets目录 await _player.play(); } // 转换播放器状态为音频服务需要的格式 PlaybackState _convertToPlaybackState(PlaybackEvent event) { return PlaybackState( controls: [MediaControl.stop], systemActions: const {MediaAction.stop}, androidCompactActionIndices: const [0], 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, ); } @override Future<void> stop() async { await _player.stop(); return super.stop(); } }
3. 修改通知调度逻辑
移除通知的sound配置,改为触发通知时启动后台音频服务:
import 'package:audio_service/audio_service.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:timezone/timezone.dart' as tz; late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; late AdhanAudioHandler _audioHandler; void main() async { WidgetsFlutterBinding.ensureInitialized(); // 初始化音频服务 _audioHandler = await AudioService.init( builder: () => AdhanAudioHandler(), config: const AudioServiceConfig( androidNotificationChannelId: 'adhan_audio_channel', androidNotificationChannelName: 'Adhan Audio', androidNotificationOngoing: true, // 保持音频通知在状态栏持续显示 ), ); // 初始化本地通知插件 flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); await flutterLocalNotificationsPlugin.initialize( InitializationSettings( android: AndroidInitializationSettings('@mipmap/ic_launcher'), iOS: DarwinInitializationSettings(), ), // 前台点击通知时触发音频播放 onDidReceiveNotificationResponse: (response) async { if (response.payload != null) { await _audioHandler.playAdhan(response.payload!); } }, ); runApp(const MyApp()); } // 调度定时通知的方法 Future<void> scheduleAdhanNotification(DateTime scheduledTime, String soundFile) async { const int insistentFlag = 4; await flutterLocalNotificationsPlugin.zonedSchedule( 1, 'Adhan Sound', '', tz.TZDateTime.from(scheduledTime, tz.local), NotificationDetails( android: AndroidNotificationDetails( 'adhan channel id', 'adhan channel name', channelDescription: 'adhan channel description', importance: Importance.max, priority: Priority.high, audioAttributesUsage: AudioAttributesUsage.alarm, additionalFlags: Int32List.fromList([insistentFlag]), // 移除sound配置,改用后台音频服务 ), iOS: DarwinNotificationDetails(sound: null), ), androidScheduleMode: AndroidScheduleMode.alarmClock, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, matchDateTimeComponents: DateTimeComponents.time, payload: soundFile, // 传递音效文件名到回调 ); }
4. 配置平台权限
Android端(AndroidManifest.xml)
添加后台服务和权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application ...> <service android:name="com.ryanheise.audioservice.AudioService"> <intent-filter> <action android:name="android.media.browse.MediaBrowserService" /> </intent-filter> </service> </application>
iOS端(Info.plist)
添加后台音频权限:
<key>UIBackgroundModes</key> <array> <string>audio</string> </array>
5. 后台触发处理(Android)
添加后台通知回调入口,确保定时通知触发时能启动音频:
@pragma('vm:entry-point') Future<void> onDidReceiveBackgroundNotificationResponse(NotificationResponse response) async { WidgetsFlutterBinding.ensureInitialized(); final audioHandler = await AudioService.init( builder: () => AdhanAudioHandler(), config: const AudioServiceConfig( androidNotificationChannelId: 'adhan_audio_channel', androidNotificationChannelName: 'Adhan Audio', androidNotificationOngoing: true, ), ); if (response.payload != null) { await audioHandler.playAdhan(response.payload!); } }
关键说明
- 通知自带的sound仅适用于短提示音,系统会在通知交互时终止其播放,无法通过修改通知参数绕过这个限制。
- 后台音频服务独立于通知生命周期,即使下拉通知栏或切换应用,音频仍会持续播放。
- 音频服务会在状态栏生成一个持续的通知(可通过配置修改样式),这是Android后台播放音频的强制要求。
内容的提问来源于stack exchange,提问作者user8
相关产品推荐
相关产品推荐

