Browse Source

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

1. 在设置语言时增加重复设置检查,避免不必要的操作
2. 调整翻译结果展示页面的UI布局和样式
3. 为图片翻译结果添加点击放大查看功能
4. 优化文本翻译页面的键盘交互行为
5. 统一调整snackbar显示时长为1秒
weicu
lxm 12 months ago
parent
commit
5a837fba94
  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: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<PictureTranslationController> {
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<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 {
try {
// 如果用户选择的语言与当前语言一致,则直接返回,避免重复设置
// print('lxm--设置语言: ${data.value}');
// print('lxm--当前语言: ${language.value.value}');
if (data.value == language.value.value) {
return;
}
Locale? targetLocale;
// 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,
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,
),
),
),

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

@ -16,23 +16,32 @@ class TextTranslationView extends GetView<TextTranslationController> {
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<TextTranslationController> {
/// 构建应用栏
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<TextTranslationController> {
),
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<TextTranslationController> {
/// 构建语言选择器(统一与其它翻译模式的样式)
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<TextTranslationController> {
),
],
),
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<TextTranslationController> {
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<TextTranslationController> {
),
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<TextTranslationController> {
],
)
: Text(
'translate'.tr,
'translate'.tr, // 翻译
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
@ -359,7 +311,7 @@ class TextTranslationView extends GetView<TextTranslationController> {
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<TextTranslationController> {
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<TextTranslationController> {
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,
),
],
),
),
),
],
),
],
),
),
],

3
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;
}

Loading…
Cancel
Save