import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../controllers/translation_controller.dart'; import '../views/translation_history_view.dart'; import '../bindings/translation_history_binding.dart'; import '../../../core/theme/app_colors.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 Scaffold( backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], appBar: _buildAppBar(isDarkMode), body: _buildBody(isDarkMode), ); } PreferredSizeWidget _buildAppBar(bool isDarkMode) { // 在同一个类中添加辅助方法 PopupMenuItem _buildPopupMenuItem(String value, String text) { return PopupMenuItem( value: value, child: Text( text, style: TextStyle( color: isDarkMode ? Colors.white : Colors.black, ), ), ); } 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(() => PopupMenuButton( onSelected: (value) { controller.changeTranslationMode(value); }, color: isDarkMode ? AppColors.cardBackground : Colors.white, itemBuilder: (BuildContext context) => [ _buildPopupMenuItem('simultaneous', 'simultaneousTranslation'.tr), _buildPopupMenuItem('faceToFace', 'faceToFaceTranslation'.tr), _buildPopupMenuItem('audioVideo', 'audioVideoTranslation'.tr), _buildPopupMenuItem('call', 'callTranslation'.tr), ], child: Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( controller.currentModeTitle.value.tr, // 添加 .value 访问实际值 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), ), Icon( Icons.arrow_drop_down, color: isDarkMode ? Colors.white : Colors.black, size: 20.sp, ), ], ), ), )), centerTitle: true, actions: [ // TTS toggle button Obx(() => IconButton( icon: Icon( controller.isRecording.value ? Icons.mic : Icons.mic_off, color: isDarkMode ? Colors.white : Colors.black, size: 24.sp, ), // onPressed: controller.toggleTts,默认播放 onPressed: controller.toggleisRecord, tooltip: '是否开启录音', )), // 历史记录按钮 IconButton( icon: Icon( Icons.history, color: isDarkMode ? Colors.white : Colors.black, size: 24.sp, ), onPressed: () async { // 停止语音识别和TTS,释放资源 await controller.stopAll(); await Get.to( () => const TranslationHistoryView(), binding: TranslationHistoryBinding(), ); // 返回时不自动开始翻译,只需重新设置滚动监听器 controller.setupScrollListener(); }, tooltip: '查看历史记录', ), ], 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: () => _showLanguageSelectionDialog(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: () => _showLanguageSelectionDialog(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(() => Text( '${controller.targetLanguage.value.split(' ')[0]}' .tr, // Just show the English part style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, )), ), ), ), ], ), ), ), ); } Widget _buildBody(bool isDarkMode) { return Column( children: [ Expanded( child: _buildTranslationContent(isDarkMode), ), _buildControlBar(isDarkMode), ], ); } Widget _buildTranslationContent(bool isDarkMode) { return Obx(() { final itemCount = controller.translationHistory.length; return ListView.builder( controller: controller.scrollController, padding: EdgeInsets.only( left: 16.w, right: 16.w, top: 16.h, bottom: 16.h, ), itemCount: itemCount, itemBuilder: (context, index) { // 获取当前项 final historyItem = controller.translationHistory[index]; // 构建项目widget return _buildTranslationItemWidget(context, historyItem); }, ); }); } Widget _buildTranslationItemWidget( BuildContext context, TranslationItem item) { return 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: [ // 源文本 Text( item.sourceText, style: TextStyle( fontSize: 16, color: Theme.of(context).textTheme.bodyLarge?.color, ), ), 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: [ 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: Icon( Icons.volume_up, size: 18, 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: 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, ), ), ); }), ], ), ); } // 显示语言选择对话框 void _showLanguageSelectionDialog(bool isSource, bool isDarkMode) { Get.dialog( Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24.r), ), insetPadding: EdgeInsets.symmetric(horizontal: 20.w), backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, child: Container( width: 0.9.sw, padding: EdgeInsets.only(top: 32.h, bottom: 16.h), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 24.w), child: Text( isSource ? 'selectSourceLanguage'.tr // 选择源语言 : 'selectTargetLanguage'.tr, // 选择目标语言 style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), ), SizedBox(height: 16.h), Container( constraints: BoxConstraints(maxHeight: 0.5.sh), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: controller.supportedLanguages.keys.map((language) { return ListTile( title: Text( '${language}'.tr, style: TextStyle( color: isDarkMode ? Colors.white : Colors.black, ), ), trailing: Obx(() { final currentLanguage = isSource ? controller.sourceLanguage.value : controller.targetLanguage.value; return currentLanguage == language ? Icon( Icons.check_circle, color: isDarkMode ? AppColors.primary : Colors.blue, ) : const SizedBox.shrink(); }), onTap: () { if (isSource) { controller.setSourceLanguage(language); } else { controller.setTargetLanguage(language); } Get.back(); }, ); }).toList(), ), ), ), SizedBox(height: 16.h), Align( alignment: Alignment.centerRight, child: Padding( padding: EdgeInsets.only(right: 24.w), child: TextButton( onPressed: () => Get.back(), child: Text( 'cancel'.tr, // 取消 style: TextStyle( color: isDarkMode ? AppColors.primary : Colors.blue, fontSize: 16.sp, ), ), ), ), ), ], ), ), ), ); } }