Browse Source

feat: 优化翻译功能交互体验和UI细节

1. 在设置语言时增加重复设置检查,避免不必要的操作
2. 调整翻译结果展示页面的UI布局和样式
3. 为图片翻译结果添加点击放大查看功能
4. 优化文本翻译页面的键盘交互行为
5. 统一调整snackbar显示时长为1秒
newdev_shunjiawei
lxm 12 months ago
parent
commit
682fed342a
  1. 73
      lib/modules/picture_translation/views/picture_translation_view.dart
  2. 7
      lib/modules/settings/controllers/settings_controller.dart
  3. 8
      lib/modules/text_translation/controllers/text_translation_controller.dart
  4. 461
      lib/modules/text_translation/views/text_translation_view.dart
  5. 3
      lib/modules/translation/middleware/controllers/translation_middleware_controller.dart

73
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:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:photo_view/photo_view.dart'; // 新增:图片查看器
import '../controllers/picture_translation_controller.dart'; import '../controllers/picture_translation_controller.dart';
import '../models/picture_translation_models.dart'; import '../models/picture_translation_models.dart';
@ -515,36 +516,41 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
SizedBox(height: 8.h), SizedBox(height: 8.h),
Container( Container(
height: 200.h, height: 200.h,
margin: EdgeInsets.symmetric(horizontal: 16.w) margin:
.copyWith(bottom: 16.h), EdgeInsets.symmetric(horizontal: 16.w).copyWith(bottom: 16.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey[100], color: Colors.grey[100],
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.grey[300]!), border: Border.all(color: Colors.grey[300]!),
), ),
child: ClipRRect( child: GestureDetector(
borderRadius: BorderRadius.circular(8.r), onTap: () => _openImageViewer(
child: Image.memory( MemoryImage(controller.translatedImageBytes.value!),
controller.translatedImageBytes.value!, ),
fit: BoxFit.cover, child: ClipRRect(
width: double.infinity, borderRadius: BorderRadius.circular(8.r),
errorBuilder: (context, error, stackTrace) { child: Image.memory(
return Center( controller.translatedImageBytes.value!,
child: Column( fit: BoxFit.cover,
mainAxisAlignment: MainAxisAlignment.center, width: double.infinity,
children: [ errorBuilder: (context, error, stackTrace) {
Icon(Icons.error_outline, return Center(
size: 48.sp, color: Colors.red[400]), child: Column(
SizedBox(height: 8.h), mainAxisAlignment: MainAxisAlignment.center,
Text( children: [
'翻译图片加载失败', Icon(Icons.error_outline,
style: TextStyle( size: 48.sp, color: Colors.red[400]),
fontSize: 14.sp, color: Colors.red[600]), SizedBox(height: 8.h),
), Text(
], '翻译图片加载失败',
), style: TextStyle(
); fontSize: 14.sp, color: Colors.red[600]),
}, ),
],
),
);
},
),
), ),
), ),
), ),
@ -972,3 +978,20 @@ class PictureTranslationView extends GetView<PictureTranslationController> {
); );
} }
} }
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),
),
),
),
));
}

7
lib/modules/settings/controllers/settings_controller.dart

@ -305,6 +305,13 @@ class SettingsController extends GetxController {
/// 设置语言 /// 设置语言
Future<void> setLanguage(LanguageModel data) async { Future<void> setLanguage(LanguageModel data) async {
try { try {
// 如果用户选择的语言与当前语言一致,则直接返回,避免重复设置
// print('lxm--设置语言: ${data.value}');
// print('lxm--当前语言: ${language.value.value}');
if (data.value == language.value.value) {
return;
}
Locale? targetLocale; Locale? targetLocale;
// print('lxm--设置语言: ${data.value}'); // print('lxm--设置语言: ${data.value}');

8
lib/modules/text_translation/controllers/text_translation_controller.dart

@ -308,7 +308,7 @@ class TextTranslationController extends GetxController {
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green.withOpacity(0.9), backgroundColor: Colors.green.withOpacity(0.9),
colorText: Colors.white, colorText: Colors.white,
duration: const Duration(seconds: 2), duration: const Duration(seconds: 1),
); );
} }
} }
@ -397,8 +397,7 @@ class TextTranslationController extends GetxController {
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? (isDarkMode ? (isDarkMode
? const Color(0xFF6200EE) ? const Color(0xFF6200EE).withOpacity(0.2)
.withOpacity(0.2)
: Colors.blue.withOpacity(0.1)) : Colors.blue.withOpacity(0.1))
: (isDarkMode : (isDarkMode
? Colors.white.withOpacity(0.05) ? Colors.white.withOpacity(0.05)
@ -472,8 +471,7 @@ class TextTranslationController extends GetxController {
child: Text( child: Text(
'cancel'.tr, // 翻译键代表的中文文本内容 'cancel'.tr, // 翻译键代表的中文文本内容
style: TextStyle( style: TextStyle(
color: color: isDarkMode ? const Color(0xFF6200EE) : Colors.blue,
isDarkMode ? const Color(0xFF6200EE) : Colors.blue,
), ),
), ),
), ),

461
lib/modules/text_translation/views/text_translation_view.dart

@ -16,23 +16,32 @@ class TextTranslationView extends GetView<TextTranslationController> {
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: _buildAppBar(isDarkMode), appBar: _buildAppBar(isDarkMode),
body: SafeArea( body: GestureDetector(
child: SingleChildScrollView( behavior: HitTestBehavior.translucent,
padding: EdgeInsets.all(16.w), onTap: () {
child: Column( // 点击空白区域收起键盘
crossAxisAlignment: CrossAxisAlignment.start, FocusManager.instance.primaryFocus?.unfocus();
children: [ },
// 输入区域 child: SafeArea(
_buildInputSection(isDarkMode), child: SingleChildScrollView(
SizedBox(height: 20.h), // 拖动时也自动收起键盘
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 输入区域
_buildInputSection(isDarkMode),
SizedBox(height: 20.h),
// 翻译按钮 // 翻译按钮
_buildTranslateButton(isDarkMode), _buildTranslateButton(isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 翻译结果区域 // 翻译结果区域
_buildResultSection(isDarkMode), _buildResultSection(isDarkMode),
], ],
),
), ),
), ),
), ),
@ -42,8 +51,8 @@ class TextTranslationView extends GetView<TextTranslationController> {
/// 构建应用栏 /// 构建应用栏
PreferredSizeWidget _buildAppBar(bool isDarkMode) { PreferredSizeWidget _buildAppBar(bool isDarkMode) {
return AppBar( return AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white, backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
elevation: 0, elevation: 1,
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
Icons.arrow_back_ios, Icons.arrow_back_ios,
@ -54,23 +63,26 @@ class TextTranslationView extends GetView<TextTranslationController> {
), ),
centerTitle: true, centerTitle: true,
title: Text( title: Text(
'textTranslation'.tr, 'textTranslation'.tr, // 文本翻译
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black, color: isDarkMode ? Colors.white : Colors.black,
), ),
overflow: TextOverflow.ellipsis,
maxLines: 1,
), ),
actions: [ // actions: [
IconButton( // IconButton(
icon: Icon( // icon: Icon(
Icons.clear_all, // Icons.clear_all,
color: isDarkMode ? Colors.white : Colors.black, // color: isDarkMode ? Colors.white : Colors.black,
), // size: 24.sp,
onPressed: controller.clearInput, // ),
tooltip: 'clearInput'.tr, // onPressed: controller.clearInput,
), // tooltip: 'clearInput'.tr,
], // ),
// ],
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: Size.fromHeight(50.h), preferredSize: Size.fromHeight(50.h),
child: Padding( child: Padding(
@ -83,154 +95,83 @@ class TextTranslationView extends GetView<TextTranslationController> {
/// 构建语言选择器(统一与其它翻译模式的样式) /// 构建语言选择器(统一与其它翻译模式的样式)
Widget _buildLanguageSelector(bool isDarkMode) { Widget _buildLanguageSelector(bool isDarkMode) {
return Container( return Row(
height: 44.h, mainAxisAlignment: MainAxisAlignment.center,
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 6.h), children: [
decoration: BoxDecoration( // 源语言选择(与面对面模式一致的胶囊样式)
color: isDarkMode ? AppColors.cardBackground : Colors.white, Expanded(
boxShadow: [ child: GestureDetector(
BoxShadow( onTap: () =>
color: Colors.black.withOpacity(0.1), controller.showLanguageSelector(isSourceLanguage: true),
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,
child: Container( child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.w), padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
padding: EdgeInsets.all(4.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode color: isDarkMode
? AppColors.primary.withOpacity(0.2) ? Colors.white.withOpacity(0.1)
: Colors.blue[50], : Colors.grey[100],
shape: BoxShape.circle, borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
Icons.swap_horiz,
color: isDarkMode ? AppColors.primary : Colors.blue,
size: 20.sp,
), ),
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( GestureDetector(
child: GestureDetector( onTap: controller.swapLanguages,
onTap: () => child: Container(
controller.showLanguageSelector(isSourceLanguage: false), margin: EdgeInsets.symmetric(horizontal: 8.w),
child: Container( padding: EdgeInsets.all(4.r),
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration(
decoration: BoxDecoration( color: isDarkMode
color: isDarkMode ? AppColors.primary.withOpacity(0.2)
? Colors.white.withOpacity(0.1) : Colors.blue[50],
: Colors.grey[100], shape: BoxShape.circle,
borderRadius: BorderRadius.circular(16.r), ),
), child: Icon(
child: Obx(() => Text( Icons.swap_horiz,
controller.targetLanguageChineseName.value, color: isDarkMode ? AppColors.primary : Colors.blue,
style: TextStyle( size: 20.sp,
fontSize: 15.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
)),
),
), ),
),
],
),
);
}
/// 构建语言选择按钮
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: [ Expanded(
Column( child: GestureDetector(
crossAxisAlignment: CrossAxisAlignment.start, onTap: () =>
children: [ controller.showLanguageSelector(isSourceLanguage: false),
Text( child: Container(
label, padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
style: TextStyle( decoration: BoxDecoration(
fontSize: 12.sp, color: isDarkMode
color: isDarkMode ? Colors.grey[400] : Colors.grey[600], ? Colors.white.withOpacity(0.1)
), : Colors.grey[100],
), borderRadius: BorderRadius.circular(16.r),
SizedBox(height: 4.h), ),
Text( child: Obx(() => Text(
selectedLanguage, controller.targetLanguageChineseName.value,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 15.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black, color: isDarkMode ? Colors.white : Colors.black,
), ),
), textAlign: TextAlign.center,
], overflow: TextOverflow.ellipsis,
), )),
Icon(
Icons.keyboard_arrow_down,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
), ),
], ),
), ),
), ],
); );
} }
@ -248,53 +189,60 @@ class TextTranslationView extends GetView<TextTranslationController> {
), ),
], ],
), ),
child: Column( child:
crossAxisAlignment: CrossAxisAlignment.start, // Column(
children: [ //crossAxisAlignment: CrossAxisAlignment.start,
Padding( // children: [
padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 8.h), // Padding(
child: Text( // padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 8.h),
'inputTextToTranslate'.tr, // child: Text(
style: TextStyle( // 'inputTextToTranslate'.tr,
fontSize: 14.sp, // style: TextStyle(
fontWeight: FontWeight.w500, // fontSize: 14.sp,
color: isDarkMode ? Colors.grey[300] : Colors.grey[700], // fontWeight: FontWeight.w500,
), // color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
), // ),
), // ),
// ),
Container( Container(
margin: EdgeInsets.symmetric(horizontal: 16.w), //margin: EdgeInsets.symmetric(horizontal: 16.w),
padding: EdgeInsets.all(12.w), padding: EdgeInsets.all(12.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.grey[50], color: isDarkMode ? Colors.grey[800] : Colors.grey[50],
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(12.r),
border: Border.all( border: Border.all(
color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, 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( border: InputBorder.none,
controller: controller.textController, focusedBorder: InputBorder.none,
maxLines: 6, counterStyle: TextStyle(
maxLength: 1000, color: isDarkMode ? Colors.grey[500] : Colors.grey[400],
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],
),
),
), ),
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<TextTranslationController> {
child: ElevatedButton( child: ElevatedButton(
onPressed: controller.isTranslating.value onPressed: controller.isTranslating.value
? null ? null
: controller.translateText, : () {
// 点击“翻译”按钮时先收起键盘
FocusManager.instance.primaryFocus?.unfocus();
controller.translateText();
},
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
foregroundColor: Colors.white, foregroundColor: Colors.white,
@ -331,7 +283,7 @@ class TextTranslationView extends GetView<TextTranslationController> {
), ),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Text( Text(
'translating'.tr, 'translating'.tr, // 正在翻译
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -340,7 +292,7 @@ class TextTranslationView extends GetView<TextTranslationController> {
], ],
) )
: Text( : Text(
'translate'.tr, 'translate'.tr, // 翻译
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -359,7 +311,7 @@ class TextTranslationView extends GetView<TextTranslationController> {
return Container( return Container(
width: double.infinity, width: double.infinity,
padding: EdgeInsets.all(16.w), // padding: EdgeInsets.all(16.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white, color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
@ -374,50 +326,6 @@ class TextTranslationView extends GetView<TextTranslationController> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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( Container(
width: double.infinity, width: double.infinity,
padding: EdgeInsets.all(12.w), padding: EdgeInsets.all(12.w),
@ -428,13 +336,44 @@ class TextTranslationView extends GetView<TextTranslationController> {
color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!,
), ),
), ),
child: Text( child: Column(
controller.translatedText.value, children: [
style: TextStyle( Text(
fontSize: 16.sp, controller.translatedText.value,
color: isDarkMode ? Colors.white : Colors.black, style: TextStyle(
height: 1.5, 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,
),
],
),
),
),
],
),
],
), ),
), ),
], ],

3
lib/modules/translation/middleware/controllers/translation_middleware_controller.dart

@ -66,7 +66,8 @@ class TranslationMiddlewareController extends GetxController {
// 翻译键代表的中文文本内容 // 翻译键代表的中文文本内容
Get.snackbar('connectDevice'.tr, Get.snackbar('connectDevice'.tr,
'pleaseConnectDeepSoundDevice'.tr, // 连接设备 / 请连接 Voitrans 设备 'pleaseConnectDeepSoundDevice'.tr, // 连接设备 / 请连接 Voitrans 设备
snackPosition: SnackPosition.TOP); snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 1));
//EasyLoading.showInfo('connectDevice'.tr); //EasyLoading.showInfo('connectDevice'.tr);
return; return;
} }

Loading…
Cancel
Save