import 'dart:ui'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:haptic_feedback/haptic_feedback.dart'; import '../controllers/translation_controller.dart'; import '../models/translation_models.dart'; import '../views/translation_history_view.dart'; import '../bindings/translation_history_binding.dart'; import '../../../core/theme/app_colors.dart'; import '../../../routes/app_routes.dart'; // 导入拆分后的组件 import 'widgets/face_to_face_layout.dart'; // 面对面翻译布局 import 'widgets/draggable_floating_button.dart'; // 可拖拽的悬浮按钮 import 'dialogs/language_selection_dialog.dart'; // 语言选择对话框 import 'dialogs/translation_mode_selector.dart'; // 翻译模式选择器 // import 'package:intl/intl.dart'; //主页面 class TranslationView extends GetView { const TranslationView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return Obx(() { // 面对面翻译模式使用特殊布局 if (controller.currentMode.value == 'faceToFace') { return _buildFaceToFaceScaffold(isDarkMode); } // 其他模式使用原有布局 return Scaffold( backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], appBar: _buildAppBar(isDarkMode), body: Stack( children: [ _buildBody(isDarkMode), // 可拖拽的悬浮按钮 DraggableFloatingButton( isDarkMode: isDarkMode, controller: controller, ), ], ), ); }); } // 构建面对面翻译模式的专用Scaffold Widget _buildFaceToFaceScaffold(bool isDarkMode) { return Scaffold( backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], body: Stack( children: [ // 主要内容区域 FaceToFaceLayout( isDarkMode: isDarkMode, controller: controller, ), // 顶部按钮(倒过来) Positioned( top: 50.h, left: 0, right: 0, child: Transform.rotate( angle: 3.14159, // 180度旋转 child: _buildFaceToFaceTopButton(isDarkMode), ), ), // 底部按钮 Positioned( bottom: 40.h, left: 0, right: 0, child: _buildFaceToFaceBottomButton(isDarkMode), ), // 可拖拽的悬浮按钮 - 在所有模式下都显示 DraggableFloatingButton( isDarkMode: isDarkMode, controller: controller, ), ], ), ); } // 构建面对面翻译顶部按钮(原右边按钮,现在移到顶部并旋转) Widget _buildFaceToFaceTopButton(bool isDarkMode) { return Center( child: Transform.rotate( angle: 0, // 180度旋转 child: _buildHoldToTalkButton( isDarkMode: isDarkMode, speakerId: 2, // 说话者2(原右边按钮) activeSpeaker: controller.activeSpeaker.value, icon: Icons.mic_none, label: controller.targetLanguage.value.split(' ')[0].tr, // 目标语言名称 ), ), ); } // 构建面对面翻译底部按钮(原左边按钮,保留在底部) Widget _buildFaceToFaceBottomButton(bool isDarkMode) { return Center( child: _buildHoldToTalkButton( isDarkMode: isDarkMode, speakerId: 1, // 说话者1(原左边按钮) activeSpeaker: controller.activeSpeaker.value, icon: Icons.mic_none, label: controller.sourceLanguage.value.split(' ')[0].tr, // 源语言名称 ), ); } PreferredSizeWidget _buildAppBar(bool isDarkMode) { return AppBar( backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, elevation: 1, leading: IconButton( icon: Icon( Icons.arrow_back_ios, color: isDarkMode ? Colors.white : Colors.black, size: 20.sp, ), onPressed: () { // Stop recognition and TTS before navigating back controller.stopAll(); Get.back(); }, ), title: Obx(() => GestureDetector( onTap: () { // 点击标题时弹出翻译模式选择器 TranslationModeSelector.show(isDarkMode); }, child: Text( controller.currentModeTitle.value.tr, style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), )), centerTitle: true, actions: [ Obx(() { if (controller.isRecording.value) { // 录音中:显示红色计时器样式,点击可停止录音 return Padding( padding: EdgeInsets.only(right: 16.w), child: GestureDetector( onTap: controller.toggleisRecord, // 点击结束录音 child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(20.r), ), child: Row( children: [ Icon( Icons.fiber_manual_record, color: Colors.white, size: 18.sp, ), SizedBox(width: 4.w), Text( controller.recordDurationText.value, style: TextStyle( color: Colors.white, fontSize: 14.sp, fontWeight: FontWeight.bold, ), ), ], ), ), ), ); } else { // 未录音:显示红色录音按钮 + 文件夹按钮 return Row( children: [ IconButton( icon: Icon( Icons.radio_button_checked, color: Colors.red, size: 24.sp, ), onPressed: controller.toggleisRecord, tooltip: 'enableRecording'.tr, // 是否开启录音 ), IconButton( icon: Icon( Icons.folder, color: isDarkMode ? Colors.white : Colors.black, size: 24.sp, ), onPressed: () async { await controller.stopAll(); Get.toNamed(Routes.translationList); controller.setupScrollListener(); }, tooltip: 'viewAudioFiles'.tr, // 查看录音文件 ), // 历史记录按钮 // IconButton( // icon: Icon( // Icons.history, // color: isDarkMode ? Colors.white : Colors.black, // size: 24.sp, // ), // onPressed: () async { // await controller.stopAll(); // Get.toNamed(Routes.translationHistory); // }, // tooltip: 'translationHistory'.tr, // 翻译历史 // ), ], ); } }), ], bottom: PreferredSize( preferredSize: Size.fromHeight(50.h), child: Padding( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 源语言选择 Expanded( child: GestureDetector( onTap: () => LanguageSelectionDialog.show(true, isDarkMode), child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], borderRadius: BorderRadius.circular(16.r), ), child: Obx(() => Text( '${controller.sourceLanguage.value.split(' ')[0]}' .tr, // Just show the Chinese part style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, )), ), ), ), // 语言互换按钮 GestureDetector( onTap: controller.swapLanguages, child: Container( margin: EdgeInsets.symmetric(horizontal: 8.w), padding: EdgeInsets.all(4.r), decoration: BoxDecoration( color: isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue[50], shape: BoxShape.circle, ), child: Icon( Icons.swap_horiz, color: isDarkMode ? AppColors.primary : Colors.blue, size: 20.sp, ), ), ), // 目标语言选择 Expanded( child: GestureDetector( onTap: () => LanguageSelectionDialog.show(false, isDarkMode), child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], borderRadius: BorderRadius.circular(16.r), ), child: Obx(() => Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Flexible( child: Text( '${controller.targetLanguage.value.split(' ')[0]}' .tr, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), ), //只在通话翻译模式下显示电话图标 if (controller.currentMode.value == 'call') ...[ SizedBox(width: 4.w), Icon( Icons.phone_in_talk, size: 20.sp, color: isDarkMode ? Colors.white70 : Colors.grey[600], ), ], ], )), ), ), ), ], ), ), ), ); } Widget _buildBody(bool isDarkMode) { return // GestureDetector( // onPanUpdate: (details) { // // 检测左滑手势 // if (details.delta.dx < -10) { // // 左滑距离超过10像素时触发 // _handleSwipeLeft(); // } // }, // child: Column( children: [ Expanded( child: _buildTranslationContent(isDarkMode), ), _buildControlBar(isDarkMode), ], ); // ); } // 处理左滑手势 // void _handleSwipeLeft() async { // await controller.stopAll(); // Get.toNamed(Routes.translationHistory); // } Widget _buildTranslationContent(bool isDarkMode) { return Obx(() { // 其他模式使用原有的列表布局 final itemCount = controller.translationHistory.length + (controller.hasMoreHistory.value ? 1 : 0); return ListView.builder( controller: controller.scrollController, reverse: true, // 🔑 关键优化:采用reverse模式,参考Agent模块 padding: EdgeInsets.only( left: 16.w, right: 16.w, top: 16.h, bottom: 16.h, ), itemCount: itemCount, itemBuilder: (context, index) { // 如果是最后一项且有更多历史记录,显示加载指示器(reverse模式下) if (index == controller.translationHistory.length && controller.hasMoreHistory.value) { return _buildLoadMoreIndicator(); } // reverse模式下需要反转索引来获取正确的历史记录 final historyIndex = controller.translationHistory.length - 1 - index; // 检查索引是否有效(reverse模式下的索引验证) if (historyIndex < 0 || historyIndex >= controller.translationHistory.length) { return const SizedBox.shrink(); } // 获取当前项 final historyItem = controller.translationHistory[historyIndex]; // 构建项目widget return _buildTranslationItemWidget(context, historyItem); }, ); }); } Widget _buildTranslationItemWidget( BuildContext context, TranslationItem item) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return GestureDetector( onLongPressStart: (details) => _onLongPressTranslationItem( context, isDarkMode, details, item, ), child: Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: item.isIntermediate ? Colors.grey.withOpacity(0.1) // 中间结果使用浅灰色底色 : (item.sourceLanguageCode == controller.sourceLanguageCode.value ? Theme.of(context) .colorScheme .primary .withOpacity(0.08) // 源语言匹配时使用主题色底色 : Theme.of(context) .colorScheme .secondary .withOpacity(0.08)), // 否则使用次要色底色 borderRadius: BorderRadius.circular(12), border: Border.all( color: item.isIntermediate ? Colors.grey.withOpacity(0.2) : Colors.transparent, width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 源文本和删除按钮行 Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 源文本 Expanded( child: Text( item.sourceText, style: TextStyle( fontSize: 16, color: Theme.of(context).textTheme.bodyLarge?.color, ), ), ), // 删除按钮 - 只对完成态记录显示,与源文本平齐 if (!item.isIntermediate) GestureDetector( onTap: () => _showDeleteItemConfirmDialog(item), child: Container( padding: EdgeInsets.all(4.w), child: Icon( Icons.delete_outline, size: 18.sp, color: Colors.grey[500], ), ), ), ], ), if (item.translatedText.isNotEmpty) ...[ const SizedBox(height: 8), // 翻译文本 Text( item.translatedText, style: TextStyle( fontSize: 16, color: Theme.of(context).textTheme.bodyMedium?.color, fontWeight: FontWeight.w500, ), ), ], // 语言信息和时间戳 const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( '${item.sourceLanguageName.tr} → ${item.targetLanguageName.tr}', style: TextStyle( fontSize: 12, color: Theme.of(context).textTheme.bodySmall?.color, ), ), ), // 播放按钮 if (!item.isIntermediate) GestureDetector( onTap: () => controller.playTranslationItem(item), child: Container( padding: EdgeInsets.all(4.w), child: Icon( Icons.volume_up, size: 20.sp, color: Theme.of(context).colorScheme.primary, ), ), ), ], ), ], ), ), ); } Widget _buildControlBar(bool isDarkMode) { // 使用GlobalKey来获取控制栏的高度 final controlBarKey = GlobalKey(); // 在构建完成后获取控制栏的高度并设置到控制器中 WidgetsBinding.instance.addPostFrameCallback((_) { if (controlBarKey.currentContext != null) { final RenderBox renderBox = controlBarKey.currentContext!.findRenderObject() as RenderBox; final height = renderBox.size.height; controller.setBottomBarHeight(height); } }); return Container( key: controlBarKey, padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), decoration: BoxDecoration( color: isDarkMode ? AppColors.cardBackground : Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withAlpha(isDarkMode ? 30 : 13), blurRadius: 4, offset: const Offset(0, -2), ), ], ), child: Obx(() { return Column( mainAxisSize: MainAxisSize.min, children: [ // 原有的按钮区域 if (controller.currentMode.value == 'faceToFace') Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 耳机图标按钮 (说话者1)-显示源语言 Obx(() => _buildHoldToTalkButton( isDarkMode: isDarkMode, speakerId: 1, activeSpeaker: controller.activeSpeaker.value, icon: Icons.mic_none, label: controller.sourceLanguage.value .split(' ')[0] .tr, // 源语言名称 )), SizedBox(width: 16.w), // 手机图标按钮 (说话者2)-显示目标语言 Obx(() => _buildHoldToTalkButton( isDarkMode: isDarkMode, speakerId: 2, activeSpeaker: controller.activeSpeaker.value, icon: Icons.mic_none, label: controller.targetLanguage.value .split(' ')[0] .tr, // 目标语言名称 )), ], ) else Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 主要控制按钮 - 开始/暂停语音识别 Obx(() { final isRecognizing = controller.isRecognizing.value; return GestureDetector( onTap: () { if (isRecognizing) { controller.stopAll(); } else { controller.startRecognition(); } }, child: Container( width: 60.w, height: 60.w, decoration: BoxDecoration( color: isRecognizing ? (isDarkMode ? Colors.red[700] : Colors.red) : (isDarkMode ? AppColors.primary : Colors.blue), shape: BoxShape.circle, ), child: Icon( isRecognizing ? Icons.pause : Icons.play_arrow, color: Colors.white, size: 30.sp, ), ), ); }), ], ), // 添加间距 //SizedBox(height: 16.h), // 语音识别结果显示框 // Container( // width: double.infinity, // padding: EdgeInsets.all(12.w), // decoration: BoxDecoration( // color: isDarkMode ? Colors.grey[800] : Colors.grey[100], // borderRadius: BorderRadius.circular(8.r), // border: Border.all( // color: isDarkMode ? Colors.grey[600]! : Colors.grey[300]!, // width: 1, // ), // ), // child: Obx(() { // // 显示最新的语音识别结果 // final recognitionText = controller.latestRecognitionText.value; // final detectedLanguage = controller.detectedLanguageCode1.value; // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // // 标题 // Text( // '麦头语音识别结果', // style: TextStyle( // fontSize: 12.sp, // color: isDarkMode ? Colors.grey[400] : Colors.grey[600], // fontWeight: FontWeight.w500, // ), // ), // SizedBox(height: 4.h), // // 识别文本 - 这里显示 finalResult1 的内容 // Text( // recognitionText.isEmpty ? '等待语音输入...' : recognitionText, // style: TextStyle( // fontSize: 14.sp, // color: isDarkMode ? Colors.white : Colors.black87, // height: 1.3, // ), // ), // // 检测到的语言(如果有) // if (detectedLanguage.isNotEmpty) ...[ // SizedBox(height: 4.h), // Text( // '检测语言: $detectedLanguage', // style: TextStyle( // fontSize: 10.sp, // color: // isDarkMode ? Colors.grey[500] : Colors.grey[500], // ), // ), // ], // ], // ); // }), // ), ], ); }), ); } // 按住说话按钮组件 Widget _buildHoldToTalkButton({ required bool isDarkMode, required int speakerId, required int activeSpeaker, required IconData icon, required String label, }) { bool isActive = activeSpeaker == speakerId; bool isDisabled = activeSpeaker != 0 && activeSpeaker != speakerId; return Listener( onPointerDown: (_) async { // 检查权限 if (!controller.hasRecordPermission.value) { Get.snackbar('权限提示', '需要麦克风权限才能使用录音功能'); return; } // 只有在没有其他按钮激活时才能激活当前按钮 if (activeSpeaker == 0) { await controller.setActiveSpeaker(speakerId); } }, onPointerUp: (_) async { // 松开按钮时,如果当前按钮是激活状态,则停止 if (isActive) { await controller.setActiveSpeaker(0); } }, child: AnimatedContainer( duration: const Duration(milliseconds: 200), width: 160.w, // 长条形宽度 height: 54.h, // 长条形高度 decoration: BoxDecoration( color: isActive ? (isDarkMode ? Colors.green[700] : Colors.green) : (isDisabled ? (isDarkMode ? Colors.grey[800] : Colors.grey[300]) : (isDarkMode ? AppColors.primary : Colors.blue)), borderRadius: BorderRadius.circular(25.r), // 圆角长条形 boxShadow: isActive ? [ BoxShadow( color: Colors.green.withOpacity(0.5), blurRadius: 10, spreadRadius: 3, ) ] : [ BoxShadow( color: (isDarkMode ? Colors.black : Colors.grey) .withOpacity(0.2), blurRadius: 8, spreadRadius: 1, offset: Offset(0, 2), ) ], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 左边图标 Icon( icon, color: isActive ? Colors.white : Colors.white.withOpacity(isDisabled ? 0.3 : 0.9), size: 20.sp, ), SizedBox(width: 8.w), // 图标和文字间距 // 右边语言名称 Flexible( child: Text( label, style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w500, color: isActive ? Colors.white : Colors.white.withOpacity(isDisabled ? 0.3 : 0.9), ), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, // 防止文本溢出 ), ), ], ), ), ); } // 构建加载更多指示器 Widget _buildLoadMoreIndicator() { return Obx(() { if (controller.isLoadingMore.value) { // 正在加载状态 - 增强视觉效果 return Container( padding: const EdgeInsets.symmetric(vertical: 20), child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2.5, valueColor: const AlwaysStoppedAnimation( AppColors.primary, ), ), ), const SizedBox(height: 12), Text( '正在加载历史记录...', style: TextStyle( fontSize: 14.sp, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), ], ), ), ); } else if (controller.isLoadingMoreError.value) { // 加载失败状态 - 增强视觉效果 return Container( padding: const EdgeInsets.symmetric(vertical: 18), child: Center( child: GestureDetector( onTap: () => controller.loadMoreHistory(), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.red[50], borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.red[200]!, width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.refresh, size: 18.sp, color: Colors.red[600], ), SizedBox(width: 8.w), Text( '加载失败,点击重试', style: TextStyle( fontSize: 14.sp, color: Colors.red[600], fontWeight: FontWeight.w500, ), ), ], ), ), ), ), ); } else { // 默认状态(有更多记录但未加载)- 增强视觉效果 return Container( padding: const EdgeInsets.symmetric(vertical: 12), child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(16), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.keyboard_arrow_up, size: 16.sp, color: Colors.grey[500], ), SizedBox(width: 4.w), Text( '向上滑动查看更多历史', style: TextStyle( fontSize: 12.sp, color: Colors.grey[600], ), ), ], ), ), ), ); } }); } /// 处理翻译条目长按事件 void _onLongPressTranslationItem( BuildContext context, bool isDarkMode, LongPressStartDetails details, TranslationItem item, ) { // 只对完成态记录显示长按菜单 if (item.isIntermediate) return; // 触发触觉反馈 Haptics.vibrate(HapticsType.success); // 计算弹出菜单位置 RenderBox? renderBox = Overlay.of(context).context.findRenderObject() as RenderBox; final RelativeRect position = RelativeRect.fromRect( Rect.fromLTRB( details.globalPosition.dx, details.globalPosition.dy, details.globalPosition.dx + 110, // 菜单显示位置X轴坐标 details.globalPosition.dy - 40, // 菜单显示位置Y轴坐标 ), Offset.zero & renderBox.size, ); // 显示弹出菜单 showMenu( context: context, position: position, color: isDarkMode ? AppColors.cardBackground : Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), items: _buildTranslationMenuItems(context, isDarkMode, item), ); } /// 构建翻译条目弹出菜单项 List _buildTranslationMenuItems( BuildContext context, bool isDarkMode, TranslationItem item, ) { return [ // 复制选项 _buildPopupMenuItem( Icon( Icons.copy, size: 20.w, color: isDarkMode ? Colors.white : Colors.black87, ), 'copy'.tr, // 复制 isDarkMode ? Colors.white : Colors.black87, () => _copyTranslationItem(item), ), // 分隔线 PopupMenuItem( height: 1, enabled: false, child: Divider( height: 1, thickness: 0.8, indent: 0, endIndent: 0, color: isDarkMode ? Colors.grey[700] : Colors.grey[300], ), ), // 删除该条记录选项 _buildPopupMenuItem( Icon( Icons.delete_outline, size: 20.w, color: isDarkMode ? Colors.orange[300] : Colors.orange, ), '删除该条记录', isDarkMode ? Colors.orange[300]! : Colors.orange, () => _showDeleteItemConfirmDialog(item), ), // 分隔线 PopupMenuItem( height: 1, enabled: false, child: Divider( height: 1, thickness: 0.8, indent: 0, endIndent: 0, color: isDarkMode ? Colors.grey[700] : Colors.grey[300], ), ), // 删除所有记录选项 _buildPopupMenuItem( Icon( Icons.delete_sweep, size: 20.w, color: Colors.red, ), '删除所有翻译历史', Colors.red, () => _showDeleteAllConfirmDialog(), ), ]; } /// 构建弹出菜单项 PopupMenuEntry _buildPopupMenuItem( Icon icon, String title, Color color, Function() onTap, ) { return PopupMenuItem( onTap: onTap, child: SizedBox( width: 150.w, child: Row( children: [ icon, 8.horizontalSpace, Text( title, style: TextStyle( color: color, fontSize: 14.sp, ), ), ], ), ), ); } /// 复制翻译条目内容 void _copyTranslationItem(TranslationItem item) { // 构建复制文本:原文 + 翻译 String copyText = item.sourceText; if (item.translatedText.isNotEmpty) { copyText += '\n${item.translatedText}'; } Clipboard.setData(ClipboardData(text: copyText)).then((_) { Get.snackbar( 'success'.tr, 'copySuccess'.tr, // 复制成功 backgroundColor: Colors.blue.withOpacity(0.8), colorText: Colors.white, snackPosition: SnackPosition.BOTTOM, duration: const Duration(milliseconds: 1200), icon: Icon( Icons.check_circle_outline, color: Colors.white, size: 24.sp, ), ); }); } // 显示删除单条记录确认对话框 void _showDeleteItemConfirmDialog(TranslationItem item) { final isDarkMode = Theme.of(Get.context!).brightness == Brightness.dark; Get.dialog( AlertDialog( backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, title: Text( 'deleteTranslationRecord'.tr, // 删除翻译记录 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'deleteTranslationConfirm'.tr, // 确定要删除这条翻译记录吗?此操作无法撤销。 style: TextStyle( fontSize: 16.sp, color: isDarkMode ? Colors.grey[300] : Colors.black87, ), ), SizedBox(height: 12.h), // 显示要删除的记录预览 Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: isDarkMode ? Colors.grey[800]?.withOpacity(0.5) : Colors.grey[100], borderRadius: BorderRadius.circular(8.r), border: Border.all( color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.sourceText, style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white : Colors.black, fontWeight: FontWeight.w500, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), if (item.translatedText.isNotEmpty) ...[ SizedBox(height: 4.h), Text( item.translatedText, style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[300] : Colors.grey[700], ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ], ), ), ], ), actions: [ TextButton( onPressed: () => Get.back(), child: Text( 'cancel'.tr, style: TextStyle( fontSize: 16.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[700], ), ), ), TextButton( onPressed: () { Get.back(); // 删除指定的翻译记录 _deleteTranslationItem(item); }, child: Text( 'confirm'.tr, style: TextStyle( fontSize: 16.sp, color: Colors.red, fontWeight: FontWeight.bold, ), ), ), ], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), ), ); } // 删除翻译记录的具体实现 void _deleteTranslationItem(TranslationItem item) { try { // 调用控制器的删除方法 controller.deleteTranslationItem(item.sessionId, item.timestamp); // 显示删除成功提示 // Get.snackbar( // 'success'.tr, // deleteSuccess // '已删除该翻译记录', // deleteTranslationSuccess // snackPosition: SnackPosition.BOTTOM, // backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200], // colorText: Get.isDarkMode ? Colors.white : Colors.black, // duration: const Duration(milliseconds: 800), // ); } catch (e) { // 显示删除失败提示 Get.snackbar( 'error'.tr, // deleteFailed '删除翻译记录时发生错误: ${e.toString()}', // deleteTranslationError snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[400], colorText: Colors.white, duration: const Duration(milliseconds: 1500), ); } } // 显示删除所有记录确认对话框 void _showDeleteAllConfirmDialog() { final isDarkMode = Theme.of(Get.context!).brightness == Brightness.dark; Get.dialog( AlertDialog( backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, title: Text( '删除所有翻译历史', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.warning_amber_rounded, color: Colors.orange, size: 24.sp, ), SizedBox(width: 8.w), Expanded( child: Text( '危险操作', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.orange, ), ), ), ], ), SizedBox(height: 12.h), Text( '确定要删除所有翻译历史记录吗?此操作将清空所有翻译记录,且无法撤销。', style: TextStyle( fontSize: 16.sp, color: isDarkMode ? Colors.grey[300] : Colors.black87, height: 1.4, ), ), SizedBox(height: 12.h), Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.red.withOpacity(0.1), borderRadius: BorderRadius.circular(8.r), border: Border.all( color: Colors.red.withOpacity(0.3), width: 1, ), ), child: Row( children: [ Icon( Icons.info_outline, color: Colors.red, size: 20.sp, ), SizedBox(width: 8.w), Expanded( child: Text( '当前共有 ${controller.translationHistory.length} 条翻译记录', style: TextStyle( fontSize: 14.sp, color: Colors.red[700], fontWeight: FontWeight.w500, ), ), ), ], ), ), ], ), actions: [ TextButton( onPressed: () => Get.back(), child: Text( 'cancel'.tr, style: TextStyle( fontSize: 16.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[700], ), ), ), TextButton( onPressed: () { Get.back(); _deleteAllTranslationHistory(); }, child: Text( '确认删除', style: TextStyle( fontSize: 16.sp, color: Colors.red, fontWeight: FontWeight.bold, ), ), ), ], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), ), ); } // 删除所有翻译历史记录的具体实现 void _deleteAllTranslationHistory() { try { // 调用控制器的删除所有记录方法 controller.clearTranslationHistory(); // 显示删除成功提示 Get.snackbar( '删除成功', '已清空所有翻译历史记录', snackPosition: SnackPosition.BOTTOM, backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200], colorText: Get.isDarkMode ? Colors.white : Colors.black, duration: const Duration(milliseconds: 1200), icon: Icon( Icons.check_circle_outline, color: Colors.green, size: 24.sp, ), ); } catch (e) { // 显示删除失败提示 Get.snackbar( 'error'.tr, '删除翻译历史时发生错误: ${e.toString()}', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[400], colorText: Colors.white, duration: const Duration(milliseconds: 1500), icon: Icon( Icons.error_outline, color: Colors.white, size: 24.sp, ), ); } } // 构建语言选择栏 Widget _buildLanguageSelectionBar(bool isDarkMode) { return Container( height: 44.h, padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 6.h), decoration: BoxDecoration( color: isDarkMode ? AppColors.cardBackground : Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2), ), BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, -2), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 源语言选择 Expanded( child: GestureDetector( onTap: () => LanguageSelectionDialog.show(true, isDarkMode), child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], borderRadius: BorderRadius.circular(16.r), ), child: Text( '${controller.sourceLanguage.value.split(' ')[0]}'.tr, style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), ), ), ), // 语言互换按钮 GestureDetector( onTap: controller.swapLanguages, child: Container( margin: EdgeInsets.symmetric(horizontal: 8.w), padding: EdgeInsets.all(4.r), decoration: BoxDecoration( color: isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue[50], shape: BoxShape.circle, ), child: Icon( Icons.swap_horiz, color: isDarkMode ? AppColors.primary : Colors.blue, size: 20.sp, ), ), ), // 目标语言选择 Expanded( child: GestureDetector( onTap: () => LanguageSelectionDialog.show(false, isDarkMode), child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], borderRadius: BorderRadius.circular(16.r), ), child: Transform.rotate( angle: 3.14159, // 旋转180度 child: Text( '${controller.targetLanguage.value.split(' ')[0]}'.tr, style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, ), ), ), ), ), ], ), ); } }