Browse Source
feat(message): 实现语音消息播放功能
feat(message): 实现语音消息播放功能
- 新增语音播放管理器 VoicePlayerManager,支持播放、暂停、停止等操作- 在聊天页面添加返回时停止播放的逻辑 -语音消息组件支持点击播放/暂停,并显示播放状态- 集成 audioplayers 库用于音频播放-优化语音消息 UI,根据播放状态切换图标 - 支持通过消息 ID 唯一标识和控制音频播放 - 添加播放失败和异常处理机制 - 更新依赖配置,引入 audioplayers 插件ios
6 changed files with 316 additions and 96 deletions
Unified View
Diff Options
-
96lib/controller/message/voice_player_manager.dart
-
136lib/pages/message/chat_page.dart
-
1lib/widget/message/message_item.dart
-
122lib/widget/message/voice_item.dart
-
56pubspec.lock
-
1pubspec.yaml
@ -0,0 +1,96 @@ |
|||||
|
import 'package:audioplayers/audioplayers.dart'; |
||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
|
/// 语音播放管理器,单例模式,统一管理语音播放 |
||||
|
class VoicePlayerManager extends GetxController { |
||||
|
static VoicePlayerManager? _instance; |
||||
|
static VoicePlayerManager get instance { |
||||
|
_instance ??= Get.put(VoicePlayerManager()); |
||||
|
return _instance!; |
||||
|
} |
||||
|
|
||||
|
final AudioPlayer _audioPlayer = AudioPlayer(); |
||||
|
String? _currentPlayingId; |
||||
|
final Rx<String?> currentPlayingId = Rx<String?>(null); |
||||
|
final Rx<PlayerState> playerState = Rx<PlayerState>(PlayerState.stopped); |
||||
|
|
||||
|
VoicePlayerManager() { |
||||
|
// 监听播放状态变化 |
||||
|
_audioPlayer.onPlayerStateChanged.listen((state) { |
||||
|
playerState.value = state; |
||||
|
if (state == PlayerState.completed) { |
||||
|
// 播放完成,重置状态 |
||||
|
_currentPlayingId = null; |
||||
|
currentPlayingId.value = null; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/// 播放音频 |
||||
|
/// [audioId] 音频的唯一标识(通常是消息ID) |
||||
|
/// [filePath] 音频文件路径 |
||||
|
Future<void> play(String audioId, String filePath) async { |
||||
|
try { |
||||
|
// 如果正在播放其他音频,先停止 |
||||
|
if (_currentPlayingId != null && _currentPlayingId != audioId) { |
||||
|
await stop(); |
||||
|
} |
||||
|
|
||||
|
// 如果是同一个音频,则暂停/恢复 |
||||
|
if (_currentPlayingId == audioId) { |
||||
|
if (playerState.value == PlayerState.playing) { |
||||
|
await _audioPlayer.pause(); |
||||
|
} else { |
||||
|
await _audioPlayer.resume(); |
||||
|
} |
||||
|
return; |
||||
|
} |
||||
|
// 播放新音频 |
||||
|
_currentPlayingId = audioId; |
||||
|
currentPlayingId.value = audioId; |
||||
|
await _audioPlayer.play(DeviceFileSource(filePath)); |
||||
|
} catch (e) { |
||||
|
print('播放音频失败: $e'); |
||||
|
_currentPlayingId = null; |
||||
|
currentPlayingId.value = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 停止播放 |
||||
|
Future<void> stop() async { |
||||
|
try { |
||||
|
await _audioPlayer.stop(); |
||||
|
_currentPlayingId = null; |
||||
|
currentPlayingId.value = null; |
||||
|
} catch (e) { |
||||
|
print('停止播放失败: $e'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 暂停播放 |
||||
|
Future<void> pause() async { |
||||
|
try { |
||||
|
await _audioPlayer.pause(); |
||||
|
} catch (e) { |
||||
|
print('暂停播放失败: $e'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 检查指定音频是否正在播放 |
||||
|
bool isPlaying(String audioId) { |
||||
|
return _currentPlayingId == audioId && |
||||
|
playerState.value == PlayerState.playing; |
||||
|
} |
||||
|
|
||||
|
/// 检查指定音频是否已加载 |
||||
|
bool isLoaded(String audioId) { |
||||
|
return _currentPlayingId == audioId; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void onClose() { |
||||
|
_audioPlayer.dispose(); |
||||
|
super.onClose(); |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save