From 5a837fba94d1c5d0c499a9cc3d47dbe166d021e4 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Thu, 30 Oct 2025 15:04:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E4=BA=A4=E4=BA=92=E4=BD=93=E9=AA=8C=E5=92=8C?= =?UTF-8?q?UI=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在设置语言时增加重复设置检查,避免不必要的操作 2. 调整翻译结果展示页面的UI布局和样式 3. 为图片翻译结果添加点击放大查看功能 4. 优化文本翻译页面的键盘交互行为 5. 统一调整snackbar显示时长为1秒 --- .../views/picture_translation_view.dart | 73 ++- .../controllers/settings_controller.dart | 7 + .../text_translation_controller.dart | 8 +- .../views/text_translation_view.dart | 461 ++++++++---------- .../translation_middleware_controller.dart | 3 +- 5 files changed, 260 insertions(+), 292 deletions(-) diff --git a/lib/modules/picture_translation/views/picture_translation_view.dart b/lib/modules/picture_translation/views/picture_translation_view.dart index e4ff23b48..9c26f50e5 100644 --- a/lib/modules/picture_translation/views/picture_translation_view.dart +++ b/lib/modules/picture_translation/views/picture_translation_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:photo_view/photo_view.dart'; // 新增:图片查看器 import '../controllers/picture_translation_controller.dart'; import '../models/picture_translation_models.dart'; @@ -515,36 +516,41 @@ class PictureTranslationView extends GetView { SizedBox(height: 8.h), Container( height: 200.h, - margin: EdgeInsets.symmetric(horizontal: 16.w) - .copyWith(bottom: 16.h), + margin: + EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.grey[300]!), ), - child: ClipRRect( - borderRadius: BorderRadius.circular(8.r), - child: Image.memory( - controller.translatedImageBytes.value!, - fit: BoxFit.cover, - width: double.infinity, - errorBuilder: (context, error, stackTrace) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.error_outline, - size: 48.sp, color: Colors.red[400]), - SizedBox(height: 8.h), - Text( - '翻译图片加载失败', - style: TextStyle( - fontSize: 14.sp, color: Colors.red[600]), - ), - ], - ), - ); - }, + child: GestureDetector( + onTap: () => _openImageViewer( + MemoryImage(controller.translatedImageBytes.value!), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.r), + child: Image.memory( + controller.translatedImageBytes.value!, + fit: BoxFit.cover, + width: double.infinity, + errorBuilder: (context, error, stackTrace) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error_outline, + size: 48.sp, color: Colors.red[400]), + SizedBox(height: 8.h), + Text( + '翻译图片加载失败', + style: TextStyle( + fontSize: 14.sp, color: Colors.red[600]), + ), + ], + ), + ); + }, + ), ), ), ), @@ -972,3 +978,20 @@ class PictureTranslationView extends GetView { ); } } + +void _openImageViewer(ImageProvider provider) { + Get.to(() => Scaffold( + body: Container( + color: Colors.black, + child: GestureDetector( + onTap: () => Get.back(), // 点击退出 + child: PhotoView( + imageProvider: provider, + minScale: PhotoViewComputedScale.contained, + maxScale: PhotoViewComputedScale.covered * 2, + backgroundDecoration: const BoxDecoration(color: Colors.black), + ), + ), + ), + )); +} diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 8084cfa29..de0b08114 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -305,6 +305,13 @@ class SettingsController extends GetxController { /// 设置语言 Future setLanguage(LanguageModel data) async { try { + // 如果用户选择的语言与当前语言一致,则直接返回,避免重复设置 + // print('lxm--设置语言: ${data.value}'); + // print('lxm--当前语言: ${language.value.value}'); + if (data.value == language.value.value) { + return; + } + Locale? targetLocale; // print('lxm--设置语言: ${data.value}'); diff --git a/lib/modules/text_translation/controllers/text_translation_controller.dart b/lib/modules/text_translation/controllers/text_translation_controller.dart index 3c7af49a2..e6f4588e7 100644 --- a/lib/modules/text_translation/controllers/text_translation_controller.dart +++ b/lib/modules/text_translation/controllers/text_translation_controller.dart @@ -308,7 +308,7 @@ class TextTranslationController extends GetxController { snackPosition: SnackPosition.TOP, backgroundColor: Colors.green.withOpacity(0.9), colorText: Colors.white, - duration: const Duration(seconds: 2), + duration: const Duration(seconds: 1), ); } } @@ -397,8 +397,7 @@ class TextTranslationController extends GetxController { decoration: BoxDecoration( color: isSelected ? (isDarkMode - ? const Color(0xFF6200EE) - .withOpacity(0.2) + ? const Color(0xFF6200EE).withOpacity(0.2) : Colors.blue.withOpacity(0.1)) : (isDarkMode ? Colors.white.withOpacity(0.05) @@ -472,8 +471,7 @@ class TextTranslationController extends GetxController { child: Text( 'cancel'.tr, // 翻译键代表的中文文本内容 style: TextStyle( - color: - isDarkMode ? const Color(0xFF6200EE) : Colors.blue, + color: isDarkMode ? const Color(0xFF6200EE) : Colors.blue, ), ), ), diff --git a/lib/modules/text_translation/views/text_translation_view.dart b/lib/modules/text_translation/views/text_translation_view.dart index 314bb78b1..0722bb111 100644 --- a/lib/modules/text_translation/views/text_translation_view.dart +++ b/lib/modules/text_translation/views/text_translation_view.dart @@ -16,23 +16,32 @@ class TextTranslationView extends GetView { return Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], appBar: _buildAppBar(isDarkMode), - body: SafeArea( - child: SingleChildScrollView( - padding: EdgeInsets.all(16.w), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 输入区域 - _buildInputSection(isDarkMode), - SizedBox(height: 20.h), + body: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + // 点击空白区域收起键盘 + FocusManager.instance.primaryFocus?.unfocus(); + }, + child: SafeArea( + child: SingleChildScrollView( + // 拖动时也自动收起键盘 + keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, + padding: EdgeInsets.all(16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 输入区域 + _buildInputSection(isDarkMode), + SizedBox(height: 20.h), - // 翻译按钮 - _buildTranslateButton(isDarkMode), - SizedBox(height: 20.h), + // 翻译按钮 + _buildTranslateButton(isDarkMode), + SizedBox(height: 20.h), - // 翻译结果区域 - _buildResultSection(isDarkMode), - ], + // 翻译结果区域 + _buildResultSection(isDarkMode), + ], + ), ), ), ), @@ -42,8 +51,8 @@ class TextTranslationView extends GetView { /// 构建应用栏 PreferredSizeWidget _buildAppBar(bool isDarkMode) { return AppBar( - backgroundColor: isDarkMode ? Colors.black : Colors.white, - elevation: 0, + backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, + elevation: 1, leading: IconButton( icon: Icon( Icons.arrow_back_ios, @@ -54,23 +63,26 @@ class TextTranslationView extends GetView { ), centerTitle: true, title: Text( - 'textTranslation'.tr, + 'textTranslation'.tr, // 文本翻译 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), + overflow: TextOverflow.ellipsis, + maxLines: 1, ), - actions: [ - IconButton( - icon: Icon( - Icons.clear_all, - color: isDarkMode ? Colors.white : Colors.black, - ), - onPressed: controller.clearInput, - tooltip: 'clearInput'.tr, - ), - ], + // actions: [ + // IconButton( + // icon: Icon( + // Icons.clear_all, + // color: isDarkMode ? Colors.white : Colors.black, + // size: 24.sp, + // ), + // onPressed: controller.clearInput, + // tooltip: 'clearInput'.tr, + // ), + // ], bottom: PreferredSize( preferredSize: Size.fromHeight(50.h), child: Padding( @@ -83,154 +95,83 @@ class TextTranslationView extends GetView { /// 构建语言选择器(统一与其它翻译模式的样式) Widget _buildLanguageSelector(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: () => - controller.showLanguageSelector(isSourceLanguage: true), - 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.sourceLanguageChineseName.value, - style: TextStyle( - fontSize: 15.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - )), - ), - ), - ), - - // 语言互换按钮(统一样式与颜色) - GestureDetector( - onTap: controller.swapLanguages, + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 源语言选择(与面对面模式一致的胶囊样式) + Expanded( + child: GestureDetector( + onTap: () => + controller.showLanguageSelector(isSourceLanguage: true), child: Container( - margin: EdgeInsets.symmetric(horizontal: 8.w), - padding: EdgeInsets.all(4.r), + padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), 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, + ? Colors.white.withOpacity(0.1) + : Colors.grey[100], + borderRadius: BorderRadius.circular(16.r), ), + child: Obx(() => Text( + controller.sourceLanguageChineseName.value, + style: TextStyle( + fontSize: 15.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + )), ), ), + ), - // 目标语言选择(与面对面模式一致的胶囊样式) - Expanded( - child: GestureDetector( - onTap: () => - controller.showLanguageSelector(isSourceLanguage: false), - 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.targetLanguageChineseName.value, - 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, ), - ), - ], - ), - ); - } - - /// 构建语言选择按钮 - Widget _buildLanguageButton({ - required String label, - required String selectedLanguage, - required VoidCallback onTap, - required bool isDarkMode, - }) { - return GestureDetector( - onTap: onTap, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[800] : Colors.grey[100], - borderRadius: BorderRadius.circular(8.r), - border: Border.all( - color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: TextStyle( - fontSize: 12.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[600], - ), - ), - SizedBox(height: 4.h), - Text( - selectedLanguage, - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.white : Colors.black, - ), - ), - ], - ), - Icon( - Icons.keyboard_arrow_down, - color: isDarkMode ? Colors.grey[400] : Colors.grey[600], + + // 目标语言选择(与面对面模式一致的胶囊样式) + Expanded( + child: GestureDetector( + onTap: () => + controller.showLanguageSelector(isSourceLanguage: false), + 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.targetLanguageChineseName.value, + style: TextStyle( + fontSize: 15.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + )), ), - ], + ), ), - ), + ], ); } @@ -248,53 +189,60 @@ class TextTranslationView extends GetView { ), ], ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 8.h), - child: Text( - 'inputTextToTranslate'.tr, - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.grey[300] : Colors.grey[700], - ), - ), - ), + child: + // Column( + //crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Padding( + // padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 8.h), + // child: Text( + // 'inputTextToTranslate'.tr, + // style: TextStyle( + // fontSize: 14.sp, + // fontWeight: FontWeight.w500, + // color: isDarkMode ? Colors.grey[300] : Colors.grey[700], + // ), + // ), + // ), Container( - margin: EdgeInsets.symmetric(horizontal: 16.w), - padding: EdgeInsets.all(12.w), - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[800] : Colors.grey[50], - borderRadius: BorderRadius.circular(8.r), - border: Border.all( - color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, - ), + //margin: EdgeInsets.symmetric(horizontal: 16.w), + padding: EdgeInsets.all(12.w), + decoration: BoxDecoration( + color: isDarkMode ? Colors.grey[800] : Colors.grey[50], + borderRadius: BorderRadius.circular(12.r), + border: Border.all( + color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, + ), + ), + child: TextField( + controller: controller.textController, + maxLines: 7, + maxLength: 1000, + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.white : Colors.black, + height: 1.5.h, + ), + decoration: InputDecoration( + hintText: 'inputTextToTranslate'.tr, // 请输入待翻译的文本 + hintStyle: TextStyle( + color: isDarkMode ? Colors.grey[500] : Colors.grey[400], ), - child: TextField( - controller: controller.textController, - maxLines: 6, - maxLength: 1000, - style: TextStyle( - fontSize: 16.sp, - color: isDarkMode ? Colors.white : Colors.black, - ), - decoration: InputDecoration( - hintText: 'inputTextToTranslate'.tr, - hintStyle: TextStyle( - color: isDarkMode ? Colors.grey[500] : Colors.grey[400], - ), - border: InputBorder.none, - counterStyle: TextStyle( - color: isDarkMode ? Colors.grey[500] : Colors.grey[400], - ), - ), + border: InputBorder.none, + focusedBorder: InputBorder.none, + counterStyle: TextStyle( + color: isDarkMode ? Colors.grey[500] : Colors.grey[400], ), + filled: true, + fillColor: isDarkMode ? Colors.grey[800] : Colors.grey[50], + contentPadding: EdgeInsets.symmetric(horizontal: 0, vertical: 0.h), + isDense: true, // 减少默认边距 ), - SizedBox(height: 16.h), - ], + ), ), + //SizedBox(height: 16.h), + // ], + // ), ); } @@ -305,7 +253,11 @@ class TextTranslationView extends GetView { child: ElevatedButton( onPressed: controller.isTranslating.value ? null - : controller.translateText, + : () { + // 点击“翻译”按钮时先收起键盘 + FocusManager.instance.primaryFocus?.unfocus(); + controller.translateText(); + }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, @@ -331,7 +283,7 @@ class TextTranslationView extends GetView { ), SizedBox(width: 12.w), Text( - 'translating'.tr, + 'translating'.tr, // 正在翻译 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, @@ -340,7 +292,7 @@ class TextTranslationView extends GetView { ], ) : Text( - 'translate'.tr, + 'translate'.tr, // 翻译 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, @@ -359,7 +311,7 @@ class TextTranslationView extends GetView { return Container( width: double.infinity, - padding: EdgeInsets.all(16.w), + // padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: isDarkMode ? Colors.grey[900] : Colors.white, borderRadius: BorderRadius.circular(12.r), @@ -374,50 +326,6 @@ class TextTranslationView extends GetView { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'translationResult'.tr, - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.grey[300] : Colors.grey[700], - ), - ), - GestureDetector( - onTap: controller.copyResult, - child: Container( - padding: - EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), - decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.1), - borderRadius: BorderRadius.circular(6.r), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.copy, - size: 16.sp, - color: Colors.blue, - ), - SizedBox(width: 4.w), - Text( - 'copyResult'.tr, - style: TextStyle( - fontSize: 12.sp, - color: Colors.blue, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ), - ], - ), - SizedBox(height: 12.h), Container( width: double.infinity, padding: EdgeInsets.all(12.w), @@ -428,13 +336,44 @@ class TextTranslationView extends GetView { color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, ), ), - child: Text( - controller.translatedText.value, - style: TextStyle( - fontSize: 16.sp, - color: isDarkMode ? Colors.white : Colors.black, - height: 1.5, - ), + child: Column( + children: [ + Text( + controller.translatedText.value, + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.white : Colors.black, + height: 1.5.h, + ), + ), + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: controller.copyResult, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 12.w, vertical: 6.h), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.1), + borderRadius: BorderRadius.circular(6.r), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.copy, + size: 16.sp, + color: Colors.blue, + ), + ], + ), + ), + ), + ], + ), + ], ), ), ], diff --git a/lib/modules/translation/middleware/controllers/translation_middleware_controller.dart b/lib/modules/translation/middleware/controllers/translation_middleware_controller.dart index c15e187e8..727ee76a7 100644 --- a/lib/modules/translation/middleware/controllers/translation_middleware_controller.dart +++ b/lib/modules/translation/middleware/controllers/translation_middleware_controller.dart @@ -66,7 +66,8 @@ class TranslationMiddlewareController extends GetxController { // 翻译键代表的中文文本内容 Get.snackbar('connectDevice'.tr, 'pleaseConnectDeepSoundDevice'.tr, // 连接设备 / 请连接 Voitrans 设备 - snackPosition: SnackPosition.TOP); + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 1)); //EasyLoading.showInfo('connectDevice'.tr); return; }