如何在Dart中通过SignalR订阅事件并触发音效?
Dart中SignalR事件订阅与音效播放的正确实现
问题背景
需要在Dart项目中通过SignalR订阅后端推送的事件,当事件触发时自动播放音效。已有JS参考实现,但当前Dart代码存在逻辑问题,无法正常工作。
示例推送数据
{ "id": "1234", "date": "2022-08-29T12:05:27.770Z", "base": "BaseNameExample", "additionInfo": "there is no example", "dataId": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }
参考JS实现
function Notifications(data) { // console.log(data) this.id = data.id; this.date = data.date; this.alarm.play(); }
现有Dart代码问题分析
- 事件参数处理错误:
hubConnection.on('GetNotifications')中使用arguments == ['id']这类无效比较,未正确解析推送的对象数据 - Logger配置冗余:重复创建Logger监听,导致代码冗余且可能出现重复输出
- 方法调用不规范:Dart方法名应遵循小驼峰规范(如
Alarm应改为alarm),且播放音效的异步操作未正确使用await - 逻辑混淆:将主动获取数据的
invoke('GetNotifications')与监听推送事件的on('GetNotifications')逻辑混在一起,订阅事件是持续监听行为,无需依赖单次invoke操作
修正后的完整代码
import 'package:signalr_core/signalr_core.dart'; import 'package:logger/logger.dart'; import 'package:audioplayers/audioplayers.dart'; class EventsSubscriber { final AudioPlayer _audioPlayer = AudioPlayer(); final Logger _logger = Logger(level: Level.all); Future<List> initializeNotifications() async { // 统一配置Logger监听 _logger.onRecord.listen((LogRecord rec) { print('${rec.level.name}: ${rec.time}: ${rec.message}'); }); final httpConnectionOptions = HttpConnectionOptions( accessTokenFactory: () => SharedPreferenceService().loginWithToken(), skipNegotiation: true, transport: HttpTransportType.webSockets, ); final hubConnection = HubConnectionBuilder() .withUrl( 'http://securelink/home', options: httpConnectionOptions, ) .build(); // 监听连接关闭事件 hubConnection.onclose(({error}) { if (error != null) { _logger.e('连接关闭: ${error.message}'); // 可选:添加自动重连逻辑 // _reconnect(hubConnection); } }); // 启动SignalR连接 await hubConnection.start(); _logger.i('SignalR连接成功'); // 主动获取历史通知(按需保留) List notifications = []; if (hubConnection.state == HubConnectionState.connected) { final result = await hubConnection.invoke('GetNotifications'); notifications = result as List; _logger.i('获取历史通知: $notifications'); // 播放初始通知音效(按需保留) await _playSound('assets/notifSound.mp3'); } // 订阅实时推送的GetNotifications事件 hubConnection.on('GetNotifications', (arguments) { if (arguments != null && arguments.isNotEmpty) { // 解析推送的第一个参数(后端推送的通知对象) final notificationData = arguments[0] as Map<String, dynamic>; final id = notificationData['id']; final date = notificationData['date']; _logger.i('收到新通知: ID=$id, Date=$date'); // 播放提醒音效 _playSound('assets/wanted.mp3'); } else { _stopSound(); } }); return notifications; } // 封装音效播放方法 Future<void> _playSound(String assetPath) async { try { await _audioPlayer.play(AssetSource(assetPath)); } catch (e) { _logger.e('播放音效失败: $e'); } } // 封装音效停止方法 Future<void> _stopSound() async { try { await _audioPlayer.stop(); } catch (e) { _logger.e('停止音效失败: $e'); } } // 可选:自动重连逻辑 // Future<void> _reconnect(HubConnection hubConnection) async { // await Future.delayed(const Duration(seconds: 5)); // try { // await hubConnection.start(); // _logger.i('重连成功'); // } catch (e) { // _logger.e('重连失败: $e'); // _reconnect(hubConnection); // } // } }
关键实现说明
- 事件参数解析:SignalR推送的事件参数以数组形式传递,第一个元素为实际数据对象,需通过
arguments[0] as Map<String, dynamic>解析 - 音效播放优化:使用
audioplayers官方推荐的AssetSource加载本地资源,封装异步播放方法并处理异常 - 连接稳定性:添加连接关闭监听,可选实现自动重连逻辑,避免因网络波动导致的监听中断
- 代码规范:遵循Dart小驼峰命名法,统一Logger配置,减少冗余代码
内容的提问来源于stack exchange,提问作者user16357470
相关产品推荐
相关产品推荐

