diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 726ebcb..f246c0d 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1,32 +1,59 @@
PODS:
- - AgoraInfra_iOS (1.2.13.1)
+ - AgoraInfra_iOS (1.2.13)
+ - camera_avfoundation (0.0.1):
+ - Flutter
- Flutter (1.0.0)
- flutter_native_splash (2.4.3):
- Flutter
- - HyphenateChat (4.15.0):
- - AgoraInfra_iOS (~> 1.2.13)
- - im_flutter_sdk_ios (4.14.0):
+ - HyphenateChat (4.15.1):
+ - AgoraInfra_iOS (= 1.2.13)
+ - im_flutter_sdk_ios (4.15.2):
- Flutter
- - HyphenateChat (= 4.15.0)
+ - HyphenateChat (= 4.15.1)
- image_picker_ios (0.0.1):
- Flutter
+ - package_info_plus (0.4.5):
+ - Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
+ - photo_manager (3.7.1):
+ - Flutter
+ - FlutterMacOS
+ - record_ios (1.1.0):
+ - Flutter
+ - sensors_plus (0.0.1):
+ - Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
+ - sqflite_darwin (0.0.4):
+ - Flutter
+ - FlutterMacOS
+ - video_player_avfoundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - wakelock_plus (0.0.1):
+ - Flutter
DEPENDENCIES:
+ - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
- Flutter (from `Flutter`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- im_flutter_sdk_ios (from `.symlinks/plugins/im_flutter_sdk_ios/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
+ - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
+ - photo_manager (from `.symlinks/plugins/photo_manager/ios`)
+ - record_ios (from `.symlinks/plugins/record_ios/ios`)
+ - sensors_plus (from `.symlinks/plugins/sensors_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
+ - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
+ - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
SPEC REPOS:
trunk:
@@ -34,6 +61,8 @@ SPEC REPOS:
- HyphenateChat
EXTERNAL SOURCES:
+ camera_avfoundation:
+ :path: ".symlinks/plugins/camera_avfoundation/ios"
Flutter:
:path: Flutter
flutter_native_splash:
@@ -42,23 +71,45 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/im_flutter_sdk_ios/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
+ package_info_plus:
+ :path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
+ photo_manager:
+ :path: ".symlinks/plugins/photo_manager/ios"
+ record_ios:
+ :path: ".symlinks/plugins/record_ios/ios"
+ sensors_plus:
+ :path: ".symlinks/plugins/sensors_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+ sqflite_darwin:
+ :path: ".symlinks/plugins/sqflite_darwin/darwin"
+ video_player_avfoundation:
+ :path: ".symlinks/plugins/video_player_avfoundation/darwin"
+ wakelock_plus:
+ :path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
- AgoraInfra_iOS: 3691b2b277a1712a35ae96de25af319de0d73d08
+ AgoraInfra_iOS: 65e11a2183ab7836258768868d06058c22701b13
+ camera_avfoundation: 281867ff09f1da66f031a184ecfbc6f2e625c9f5
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
- HyphenateChat: 4523c7fb2075771c49a2c492b31544d6cc82ff50
- im_flutter_sdk_ios: 2348d34baa17e98d8c490d92023410956c8afee1
+ HyphenateChat: ec813941100d602d24e06b04b867474d634cb39d
+ im_flutter_sdk_ios: de87814fcf3a3cb585a78b55fba5f9fec989096b
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
+ package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
+ photo_manager: 81954a1bf804b6e882d0453b3b6bc7fad7b47d3d
+ record_ios: 840d21cce013c5a3b2168b74a54ebdb4136359e2
+ sensors_plus: 7229095999f30740798f0eeef5cd120357a8f4f2
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
+ sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
+ video_player_avfoundation: 7993f492ae0bd77edaea24d9dc051d8bb2cd7c86
+ wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d9810..0000000
--- a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
deleted file mode 100644
index f9b0d7c..0000000
--- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- PreviewsEnabled
-
-
-
diff --git a/lib/controller/message/voice_player_manager.dart b/lib/controller/message/voice_player_manager.dart
new file mode 100644
index 0000000..0f498c6
--- /dev/null
+++ b/lib/controller/message/voice_player_manager.dart
@@ -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 currentPlayingId = Rx(null);
+ final Rx playerState = Rx(PlayerState.stopped);
+
+ VoicePlayerManager() {
+ // 监听播放状态变化
+ _audioPlayer.onPlayerStateChanged.listen((state) {
+ playerState.value = state;
+ if (state == PlayerState.completed) {
+ // 播放完成,重置状态
+ _currentPlayingId = null;
+ currentPlayingId.value = null;
+ }
+ });
+ }
+
+ /// 播放音频
+ /// [audioId] 音频的唯一标识(通常是消息ID)
+ /// [filePath] 音频文件路径
+ Future 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 stop() async {
+ try {
+ await _audioPlayer.stop();
+ _currentPlayingId = null;
+ currentPlayingId.value = null;
+ } catch (e) {
+ print('停止播放失败: $e');
+ }
+ }
+
+ /// 暂停播放
+ Future 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();
+ }
+}
diff --git a/lib/pages/message/chat_page.dart b/lib/pages/message/chat_page.dart
index 79425b0..3c9f60a 100644
--- a/lib/pages/message/chat_page.dart
+++ b/lib/pages/message/chat_page.dart
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../controller/message/chat_controller.dart';
+import '../../controller/message/voice_player_manager.dart';
import '../../generated/assets.dart';
import '../../../widget/message/chat_input_bar.dart';
import '../../../widget/message/message_item.dart';
@@ -19,59 +20,65 @@ class ChatPage extends StatelessWidget {
return GetBuilder(
init: ChatController(userId: userId),
builder: (controller) {
- return Scaffold(
- backgroundColor: Color(0xffF5F5F5),
- appBar: AppBar(
- title: Text(controller.userInfo.value?.nickName ?? ''),
- centerTitle: true,
- actions: [
- Container(
- padding: EdgeInsets.only(right: 16.w),
- child: Image.asset(Assets.imagesMore, width: 16.w),
- ).onTap(() {}),
- ],
- leading: IconButton(
- icon: Icon(Icons.arrow_back_ios),
- onPressed: () {
- Get.back();
- },
+ return WillPopScope(
+ onWillPop: () async {
+ // 退出页面时停止播放并销毁播放器
+ await VoicePlayerManager.instance.stop();
+ return true;
+ },
+ child: Scaffold(
+ backgroundColor: Color(0xffF5F5F5),
+ appBar: AppBar(
+ title: Text(controller.userInfo.value?.nickName ?? ''),
+ centerTitle: true,
+ actions: [
+ Container(
+ padding: EdgeInsets.only(right: 16.w),
+ child: Image.asset(Assets.imagesMore, width: 16.w),
+ ).onTap(() {}),
+ ],
+ leading: IconButton(
+ icon: Icon(Icons.arrow_back_ios),
+ onPressed: () {
+ Get.back();
+ },
+ ),
),
- ),
- body: Column(
- children: [
- // 消息列表区域
- Expanded(
- child: Container(
- color: Color(0xffF5F5F5),
- child: GestureDetector(
- onTap: () {
- // 点击消息区域收起键盘
- FocusManager.instance.primaryFocus?.unfocus();
- },
- behavior: HitTestBehavior.opaque,
- child: ListView.builder(
- reverse: true,
- padding: EdgeInsets.all(16.w),
- itemCount: controller.messages.length,
- itemBuilder: (context, index) {
- final message = controller.messages[index];
- final isSentByMe =
- message.direction == MessageDirection.SEND;
+ body: Column(
+ children: [
+ // 消息列表区域
+ Expanded(
+ child: Container(
+ color: Color(0xffF5F5F5),
+ child: GestureDetector(
+ onTap: () {
+ // 点击消息区域收起键盘
+ FocusManager.instance.primaryFocus?.unfocus();
+ },
+ behavior: HitTestBehavior.opaque,
+ child: ListView.builder(
+ reverse: true,
+ padding: EdgeInsets.all(16.w),
+ itemCount: controller.messages.length,
+ itemBuilder: (context, index) {
+ final message = controller.messages[index];
+ final isSentByMe =
+ message.direction == MessageDirection.SEND;
- final previousMessage = index > 0
- ? controller.messages[index - 1]
- : null;
+ final previousMessage = index > 0
+ ? controller.messages[index - 1]
+ : null;
- return MessageItem(
- message: message,
- isSentByMe: isSentByMe,
- previousMessage: previousMessage,
- );
- },
+ return MessageItem(
+ message: message,
+ isSentByMe: isSentByMe,
+ previousMessage: previousMessage,
+ );
+ },
+ ),
),
),
),
- ),
// 使用抽离的聊天输入栏组件
ChatInputBar(
onSendMessage: (message) async {
@@ -97,7 +104,8 @@ class ChatPage extends StatelessWidget {
),
],
),
- );
+ ),
+ );
},
);
}
diff --git a/lib/widget/message/message_item.dart b/lib/widget/message/message_item.dart
index bf6dc0d..661d7ec 100644
--- a/lib/widget/message/message_item.dart
+++ b/lib/widget/message/message_item.dart
@@ -48,6 +48,7 @@ class MessageItem extends StatelessWidget {
final voiceBody = message.body as EMVoiceMessageBody;
return VoiceItem(
voiceBody: voiceBody,
+ messageId: message.msgId, // 传递消息ID作为音频唯一标识
isSentByMe: isSentByMe,
showTime: shouldShowTime(),
formattedTime: formatMessageTime(message.serverTime),
diff --git a/lib/widget/message/voice_item.dart b/lib/widget/message/voice_item.dart
index c9f6e3c..3e91478 100644
--- a/lib/widget/message/voice_item.dart
+++ b/lib/widget/message/voice_item.dart
@@ -1,53 +1,110 @@
+import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../../generated/assets.dart';
+import '../../../controller/message/voice_player_manager.dart';
-class VoiceItem extends StatelessWidget {
+class VoiceItem extends StatefulWidget {
final EMVoiceMessageBody voiceBody;
+ final String messageId; // 消息ID,用作音频的唯一标识
final bool isSentByMe;
final bool showTime;
final String formattedTime;
const VoiceItem({
required this.voiceBody,
+ required this.messageId,
required this.isSentByMe,
required this.showTime,
required this.formattedTime,
super.key,
});
+ @override
+ State createState() => _VoiceItemState();
+}
+
+class _VoiceItemState extends State {
+ final VoicePlayerManager _playerManager = VoicePlayerManager.instance;
+
+ @override
+ void initState() {
+ super.initState();
+ // 监听播放状态变化
+ ever(_playerManager.currentPlayingId, (audioId) {
+ if (mounted) {
+ setState(() {});
+ }
+ });
+ ever(_playerManager.playerState, (state) {
+ if (mounted) {
+ setState(() {});
+ }
+ });
+ }
+
+ // 处理播放/暂停
+ Future _handlePlayPause() async {
+ try {
+ // 获取音频文件路径
+ String? filePath;
+ final localPath = widget.voiceBody.localPath;
+ final remotePath = widget.voiceBody.remotePath;
+
+ if (localPath.isNotEmpty) {
+ filePath = localPath;
+ } else if (remotePath != null && remotePath.isNotEmpty) {
+ // 如果是远程路径,需要先下载(这里简化处理,实际应该先下载到本地)
+ filePath = remotePath;
+ }
+ SmartDialog.showToast('来了$remotePath');
+ if (filePath != null && filePath.isNotEmpty) {
+ await _playerManager.play(widget.messageId, filePath);
+ } else {
+ print('音频文件路径为空');
+ }
+ } catch (e) {
+ print('播放音频失败: $e');
+ }
+ }
+
@override
Widget build(BuildContext context) {
// 获取语音时长(秒)
- final duration = voiceBody.duration;
+ final duration = widget.voiceBody.duration;
final durationText = '${duration}s';
+ // 判断当前音频是否正在播放
+ final isPlaying = _playerManager.isPlaying(widget.messageId);
+
return Column(
children: [
// 显示时间
- if (showTime) _buildTimeLabel(),
+ if (widget.showTime) _buildTimeLabel(),
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
child: Row(
- mainAxisAlignment: isSentByMe
+ mainAxisAlignment: widget.isSentByMe
? MainAxisAlignment.end
: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if (!isSentByMe) _buildAvatar(),
- if (!isSentByMe) SizedBox(width: 8.w),
+ if (!widget.isSentByMe) _buildAvatar(),
+ if (!widget.isSentByMe) SizedBox(width: 8.w),
Container(
margin: EdgeInsets.only(top: 10.h),
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.h),
decoration: BoxDecoration(
- color: isSentByMe ? Color(0xff8E7BF6) : Colors.white,
+ color: widget.isSentByMe ? Color(0xff8E7BF6) : Colors.white,
borderRadius: BorderRadius.only(
- topLeft: isSentByMe
+ topLeft: widget.isSentByMe
? Radius.circular(12.w)
: Radius.circular(0),
- topRight: isSentByMe
+ topRight: widget.isSentByMe
? Radius.circular(0)
: Radius.circular(12.w),
bottomLeft: Radius.circular(12.w),
@@ -58,22 +115,21 @@ class VoiceItem extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
// 播放按钮
- GestureDetector(
- onTap: () {
- // TODO: 处理播放/暂停逻辑
- },
- child: Container(
- width: 20.w,
- height: 20.w,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: isSentByMe ? Colors.white : Colors.black,
- ),
- child: Icon(
- Icons.play_arrow,
- color: isSentByMe ? Color(0xff8E7BF6) : Colors.white,
- size: 16.w,
- ),
+ Container(
+ width: 20.w,
+ height: 20.w,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: widget.isSentByMe
+ ? Colors.white
+ : Colors.black,
+ ),
+ child: Icon(
+ isPlaying ? Icons.pause : Icons.play_arrow,
+ color: widget.isSentByMe
+ ? Color(0xff8E7BF6)
+ : Colors.white,
+ size: 16.w,
),
),
SizedBox(width: 8.w),
@@ -82,7 +138,7 @@ class VoiceItem extends StatelessWidget {
durationText,
style: TextStyle(
fontSize: 14.sp,
- color: isSentByMe ? Colors.white : Colors.black,
+ color: widget.isSentByMe ? Colors.white : Colors.black,
fontWeight: FontWeight.w500,
),
),
@@ -91,9 +147,11 @@ class VoiceItem extends StatelessWidget {
_buildWaveform(),
],
),
- ),
- if (isSentByMe) SizedBox(width: 8.w),
- if (isSentByMe) _buildAvatar(),
+ ).onTap(() {
+ _handlePlayPause();
+ }),
+ if (widget.isSentByMe) SizedBox(width: 8.w),
+ if (widget.isSentByMe) _buildAvatar(),
],
),
),
@@ -109,7 +167,7 @@ class VoiceItem extends StatelessWidget {
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Text(
- formattedTime,
+ widget.formattedTime,
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
),
),
@@ -134,7 +192,7 @@ class VoiceItem extends StatelessWidget {
// 构建音频波形
Widget _buildWaveform() {
// 根据时长生成波形条数量(最多20个)
- final barCount = (voiceBody.duration / 2).ceil().clamp(5, 20);
+ final barCount = (widget.voiceBody.duration / 2).ceil().clamp(5, 20);
return SizedBox(
height: 16.h,
@@ -152,7 +210,7 @@ class VoiceItem extends StatelessWidget {
height: height,
margin: EdgeInsets.symmetric(horizontal: 1.w),
decoration: BoxDecoration(
- color: isSentByMe
+ color: widget.isSentByMe
? Colors.white.withOpacity(0.8)
: Colors.grey.withOpacity(0.6),
borderRadius: BorderRadius.circular(1.w),
diff --git a/pubspec.lock b/pubspec.lock
index ea8a520..6032ac6 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -49,6 +49,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.13.0"
+ audioplayers:
+ dependency: "direct main"
+ description:
+ name: audioplayers
+ sha256: "5441fa0ceb8807a5ad701199806510e56afde2b4913d9d17c2f19f2902cf0ae4"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.5.1"
+ audioplayers_android:
+ dependency: transitive
+ description:
+ name: audioplayers_android
+ sha256: "60a6728277228413a85755bd3ffd6fab98f6555608923813ce383b190a360605"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.2.1"
+ audioplayers_darwin:
+ dependency: transitive
+ description:
+ name: audioplayers_darwin
+ sha256: "0811d6924904ca13f9ef90d19081e4a87f7297ddc19fc3d31f60af1aaafee333"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.3.0"
+ audioplayers_linux:
+ dependency: transitive
+ description:
+ name: audioplayers_linux
+ sha256: f75bce1ce864170ef5e6a2c6a61cd3339e1a17ce11e99a25bae4474ea491d001
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "4.2.1"
+ audioplayers_platform_interface:
+ dependency: transitive
+ description:
+ name: audioplayers_platform_interface
+ sha256: "0e2f6a919ab56d0fec272e801abc07b26ae7f31980f912f24af4748763e5a656"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "7.1.1"
+ audioplayers_web:
+ dependency: transitive
+ description:
+ name: audioplayers_web
+ sha256: "1c0f17cec68455556775f1e50ca85c40c05c714a99c5eb1d2d57cc17ba5522d7"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.1.1"
+ audioplayers_windows:
+ dependency: transitive
+ description:
+ name: audioplayers_windows
+ sha256: "4048797865105b26d47628e6abb49231ea5de84884160229251f37dfcbe52fd7"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "4.2.1"
boolean_selector:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 4279654..e3ea775 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -61,6 +61,7 @@ dependencies:
record: ^6.1.2
video_player: ^2.9.2
chewie: ^1.8.5 # 视频播放器UI
+ audioplayers: ^6.5.1
dev_dependencies:
flutter_test: