From f8b9846963f0c62b2f35fa5e86035c488e4e1091 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Fri, 21 Mar 2025 15:22:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BC=9A=E8=AE=AE=E5=8A=A9?= =?UTF-8?q?=E6=89=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/meeting_controller.dart | 79 +++++++++++ lib/modules/meeting/views/meeting_view.dart | 133 +++++++++++++++--- pubspec.yaml | 1 + 3 files changed, 193 insertions(+), 20 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_controller.dart b/lib/modules/meeting/controllers/meeting_controller.dart index acd45276f..f80e033db 100644 --- a/lib/modules/meeting/controllers/meeting_controller.dart +++ b/lib/modules/meeting/controllers/meeting_controller.dart @@ -1,19 +1,98 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import '../../../data/services/asr_service.dart'; + class MeetingController extends GetxController with GetSingleTickerProviderStateMixin { late AnimationController lottieController; + late AsrService _asrService; + + Timer? timer; + + RxBool isRecording = false.obs; //是否录音中 + RxInt seconds = 0.obs; + + RxString intermediateContent = ''.obs; + RxString finalContent = ''.obs; + + StreamSubscription? _recognitionSubscription; @override void onInit() { super.onInit(); lottieController = AnimationController(vsync: this); + _asrService = Get.find(); } @override void onClose() { lottieController.dispose(); + timer?.cancel(); + _asrService.stopContinuousRecognition(); + _recognitionSubscription?.cancel(); super.onClose(); } + + // 点击开始、暂停按钮 + void recorderHandle() { + if (isRecording.value) { + _pause(); + } else { + _start(); + } + } + + // 开始 + void _start() async { + await _startRecorder(); + lottieController.repeat(); + isRecording.value = true; + timer = Timer.periodic(const Duration(seconds: 1), (_) => seconds++); + } + + // 暂停 + void _pause() async { + await _stopRecorder(); + lottieController.stop(); + isRecording.value = false; + timer?.cancel(); + } + + //开始录音 + Future _startRecorder() async { + final recognitionStream = await _asrService.startContinuousRecognition(); + _recognitionSubscription = recognitionStream.listen( + _handleRecognitionEvent, + ); + } + + //结束录音 + Future _stopRecorder() async { + await _asrService.stopContinuousRecognition(); + _recognitionSubscription?.cancel(); + } + + // 处理语音识别事件 + void _handleRecognitionEvent(RecognitionEvent event) { + switch (event.type) { + case RecognitionEventType.intermediateResult: + intermediateContent.value = event.text; + break; + case RecognitionEventType.finalResult: + finalContent.value += event.text; + intermediateContent.value = ''; + break; + default: + // 不做任何处理 + break; + } + } + + // 完成 + void complete() async { + Get.back(); + } } diff --git a/lib/modules/meeting/views/meeting_view.dart b/lib/modules/meeting/views/meeting_view.dart index e48d03c2d..3e77eff37 100644 --- a/lib/modules/meeting/views/meeting_view.dart +++ b/lib/modules/meeting/views/meeting_view.dart @@ -1,3 +1,4 @@ +import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; @@ -13,10 +14,17 @@ class MeetingView extends GetView { Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - return Scaffold( - backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], - appBar: _buildAppBar(isDarkMode), - body: _buildBody(isDarkMode), + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) { + if (didPop) return; + _showAlertDialog(isDarkMode); + }, + child: Scaffold( + backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], + appBar: _buildAppBar(isDarkMode), + body: _buildBody(isDarkMode), + ), ); } @@ -31,24 +39,46 @@ class MeetingView extends GetView { color: isDarkMode ? Colors.white : Colors.black, size: 20.sp, ), - onPressed: () => Get.back(), + onPressed: () { + _showAlertDialog(isDarkMode); + }, ), title: Padding( padding: const EdgeInsets.only(right: 56), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.keyboard_voice, - color: isDarkMode ? Colors.white : Colors.black, + Obx( + () => Icon( + Icons.keyboard_voice, + color: isDarkMode + ? controller.seconds.value > 0 + ? AppColors.primary + : Colors.white + : controller.seconds.value > 0 + ? Colors.blue + : Colors.black, + ), ), 5.horizontalSpace, - Text( - '00:00:00', - style: TextStyle( - fontSize: 17.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, + Obx( + () => Text( + DateUtil.formatDateMs( + controller.seconds.value * 1000, + format: 'HH:mm:ss', + isUtc: true, + ), + style: TextStyle( + fontSize: 17.sp, + fontWeight: FontWeight.w600, + color: isDarkMode + ? controller.seconds.value > 0 + ? AppColors.primary + : Colors.white + : controller.seconds.value > 0 + ? Colors.blue + : Colors.black, + ), ), ), ], @@ -174,6 +204,17 @@ class MeetingView extends GetView { ), ], ), + child: Align( + alignment: Alignment.topLeft, + child: SingleChildScrollView( + reverse: true, + child: Obx( + () => Text( + '${controller.finalContent.value}${controller.intermediateContent.value}', + ), + ), + ), + ), ), ); } @@ -186,7 +227,7 @@ class MeetingView extends GetView { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( - onTap: () {}, + onTap: controller.recorderHandle, child: Container( width: 130.w, height: 60.w, @@ -197,15 +238,19 @@ class MeetingView extends GetView { width: 2.w, ), ), - child: Icon( - Icons.pause, - color: isDarkMode ? AppColors.primary : Colors.blue, - size: 40.w, + child: Obx( + () => Icon( + controller.isRecording.value ? Icons.pause : Icons.play_arrow, + color: isDarkMode ? AppColors.primary : Colors.blue, + size: 40.w, + ), ), ), ), GestureDetector( - onTap: () {}, + onTap: () { + _showAlertDialog(isDarkMode); + }, child: Container( width: 130.w, height: 60.w, @@ -224,4 +269,52 @@ class MeetingView extends GetView { ), ); } + + // 显示对话框 + void _showAlertDialog(bool isDarkMode) { + if (controller.seconds.value > 0) { + Get.dialog( + AlertDialog( + backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white, + title: Text( + '注意', + style: TextStyle( + color: isDarkMode ? Colors.white : Colors.black87, + ), + ), + content: Text( + '你想结束录音吗?', + style: TextStyle( + color: isDarkMode ? Colors.white70 : Colors.black54, + ), + ), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + 'cancel'.tr, // 取消 + style: TextStyle( + color: isDarkMode ? Colors.white70 : Colors.black54, + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + controller.complete(); + }, + child: Text( + 'confirm'.tr, // 确定 + style: TextStyle( + color: isDarkMode ? Colors.red[300] : Colors.red[600], + ), + ), + ), + ], + ), + ); + } else { + Get.back(); + } + } } diff --git a/pubspec.yaml b/pubspec.yaml index 799493343..e32a8fd9d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,7 @@ dependencies: firebase_auth: ^5.5.0 google_sign_in: ^6.2.2 flutter_facebook_auth: ^7.1.1 + common_utils: ^2.1.0 dev_dependencies: flutter_test: