Browse Source

refactor(translation): 重构翻译模式选择器的显示逻辑

移除手动计算锚点的冗余代码,统一从悬浮按钮位置展开
优化选择器动画和背景处理,提升用户体验
newdev_shunjiawei
lxm 1 year ago
parent
commit
e0d5fabf3b
  1. 24
      lib/modules/translation/controllers/translation_controller.dart
  2. 68
      lib/modules/translation/views/dialogs/translation_mode_selector.dart
  3. 18
      lib/modules/translation/views/translation_view.dart
  4. 10
      lib/modules/translation/views/widgets/draggable_floating_button.dart

24
lib/modules/translation/controllers/translation_controller.dart

@ -23,6 +23,7 @@ import '../../../data/services/ble_manager.dart';
import 'record_timer_controller.dart';
import 'translation_history_controller.dart';
import '../models/translation_models.dart';
import '../views/dialogs/translation_mode_selector.dart';
/// 音频输出设备枚举
enum AudioOutputDevice {
@ -285,6 +286,17 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
// 监听应用生命周期:当应用切到后台/失焦时兜底重置 activeSpeaker
WidgetsBinding.instance.addObserver(this);
// 延迟显示模式选择器,确保页面已经完全加载
// Future.delayed(const Duration(milliseconds: 100), () {
// _showModeSelector();
// });
//确保关键服务初始化完成
WidgetsBinding.instance.addPostFrameCallback((_) async {
// 确保上下文有效
if (Get.context == null || !Get.context!.mounted) return;
_showModeSelector();
});
}
/// 预先检查权限
@ -339,6 +351,18 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
}
}
/// 显示模式选择器
void _showModeSelector() {
try {
final isDarkMode = Get.theme.brightness == Brightness.dark;
// 直接使用现有的翻译模式选择器组件
TranslationModeSelector.show(isDarkMode);
} catch (e) {
Logger.error('显示模式选择器失败: $e');
}
}
/// 保存悬浮按钮位置
void saveFloatingButtonPosition(Offset position) {
floatingButtonPosition.value = position;

68
lib/modules/translation/views/dialogs/translation_mode_selector.dart

@ -11,20 +11,25 @@ class TranslationModeSelector {
static void show(bool isDarkMode, {Offset? anchor}) {
final controller = Get.find<TranslationController>();
// 计算缩放对齐点(-1..1)。如果未提供 anchor,则从底部中间展开
Alignment alignment = Alignment.bottomCenter;
if (anchor != null) {
final Size screenSize = MediaQuery.of(Get.context!).size;
final double ax = (anchor.dx / screenSize.width) * 2 - 1;
final double ay = (anchor.dy / screenSize.height) * 2 - 1;
alignment = Alignment(ax, ay);
}
// 总是从悬浮按钮位置展开,不依赖传入的anchor参数
final Size screenSize = MediaQuery.of(Get.context!).size;
final Offset floatingButtonPos = controller.floatingButtonPosition.value;
// 计算悬浮按钮的中心点位置(从右下角偏移转换为屏幕坐标)
final double buttonSize = 56.0; // 悬浮按钮的大小
final double buttonCenterX = screenSize.width - floatingButtonPos.dx - buttonSize / 2;
final double buttonCenterY = screenSize.height - floatingButtonPos.dy - buttonSize / 2;
// 计算缩放对齐点(-1..1)
final double ax = (buttonCenterX / screenSize.width) * 2 - 1;
final double ay = (buttonCenterY / screenSize.height) * 2 - 1;
final Alignment alignment = Alignment(ax, ay);
Get.generalDialog(
barrierLabel: 'translation_mode_selector',
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.35),
transitionDuration: const Duration(milliseconds: 250),
barrierDismissible: true, // 允许点击背景关闭
barrierColor: Colors.transparent, // 设置为透明,让自定义模糊层处理点击
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) {
return const SizedBox.shrink();
},
@ -35,17 +40,22 @@ class TranslationModeSelector {
reverseCurve: Curves.easeInCubic);
return Stack(
children: [
// 模糊背景
Positioned.fill(
// 修复:将模糊背景放在可点击区域
GestureDetector(
onTap: () => Get.back(), // 添加点击关闭功能
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(color: Colors.transparent),
child: Container(
color: isDarkMode
? Colors.black.withOpacity(0.55)
: Colors.grey.withOpacity(0.95), // 添加半透明背景
),
),
),
// 将缩放应用在一个全屏容器上,以便 alignment 以屏幕为参考
// 内容区域
ScaleTransition(
alignment: alignment,
scale: Tween<double>(begin: 0.0, end: 1.0).animate(curved),
scale: Tween<double>(begin: 0.1, end: 1.0).animate(curved),
child: FadeTransition(
opacity: curved,
child: SizedBox.expand(
@ -127,19 +137,19 @@ class TranslationModeSelector {
}
: null,
),
SizedBox(height: 24.w),
GestureDetector(
onTap: () => Get.back(),
child: Container(
width: 42,
height: 42,
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.8),
borderRadius: BorderRadius.circular(50),
),
child: const Icon(Icons.close, color: Colors.white),
),
),
SizedBox(height: 64.w),
// GestureDetector(
// onTap: () => Get.back(),
// child: Container(
// width: 42,
// height: 42,
// decoration: BoxDecoration(
// color: Colors.black.withValues(alpha: 0.8),
// borderRadius: BorderRadius.circular(50),
// ),
// child: const Icon(Icons.close, color: Colors.white),
// ),
// ),
if (Platform.isAndroid) SizedBox(height: 20.h),
],
),

18
lib/modules/translation/views/translation_view.dart

@ -145,15 +145,15 @@ class TranslationView extends GetView<TranslationController> {
onTap: () {
// 点击标题时弹出翻译模式选择器
// 从拖拽按钮位置作为锚点打开
final pos = controller.floatingButtonPosition.value;
final Size screen = MediaQuery.of(Get.context!).size;
// 已保存的是距右、下的偏移,需要换算为屏幕坐标中的中心点
final double buttonSize = 56.w;
final double left = screen.width - pos.dx - buttonSize;
final double top = screen.height - pos.dy - buttonSize;
final Offset anchor =
Offset(left + buttonSize / 2, top + buttonSize / 2);
TranslationModeSelector.show(isDarkMode, anchor: anchor);
// final pos = controller.floatingButtonPosition.value;
// final Size screen = MediaQuery.of(Get.context!).size;
// // 已保存的是距右、下的偏移,需要换算为屏幕坐标中的中心点
// final double buttonSize = 56.w;
// final double left = screen.width - pos.dx - buttonSize;
// final double top = screen.height - pos.dy - buttonSize;
// final Offset anchor =
// Offset(left + buttonSize / 2, top + buttonSize / 2);
TranslationModeSelector.show(isDarkMode);
},
child: Text(
controller.currentModeTitle.value.tr,

10
lib/modules/translation/views/widgets/draggable_floating_button.dart

@ -108,13 +108,7 @@ class DraggableFloatingButton extends StatelessWidget {
/// 显示翻译模式选择器
void _showTranslationModeSelector(bool isDarkMode) {
// 使用保存的位置换算按钮中心点(与标题点击一致)
final pos = controller.floatingButtonPosition.value; // 距右、下的偏移
final Size screen = MediaQuery.of(Get.context!).size;
final double buttonSize = 56.w;
final double left = screen.width - pos.dx - buttonSize;
final double top = screen.height - pos.dy - buttonSize;
final Offset anchor = Offset(left + buttonSize / 2, top + buttonSize / 2);
TranslationModeSelector.show(isDarkMode, anchor: anchor);
// 直接调用模式选择器,它会自动从悬浮按钮位置展开
TranslationModeSelector.show(isDarkMode);
}
}

Loading…
Cancel
Save