Browse Source

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

移除手动计算锚点的冗余代码,统一从悬浮按钮位置展开
优化选择器动画和背景处理,提升用户体验
weicu
lxm 1 year ago
parent
commit
ab7f02e77e
  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 'record_timer_controller.dart';
import 'translation_history_controller.dart'; import 'translation_history_controller.dart';
import '../models/translation_models.dart'; import '../models/translation_models.dart';
import '../views/dialogs/translation_mode_selector.dart';
/// 音频输出设备枚举 /// 音频输出设备枚举
enum AudioOutputDevice { enum AudioOutputDevice {
@ -285,6 +286,17 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
// 监听应用生命周期:当应用切到后台/失焦时兜底重置 activeSpeaker // 监听应用生命周期:当应用切到后台/失焦时兜底重置 activeSpeaker
WidgetsBinding.instance.addObserver(this); 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) { void saveFloatingButtonPosition(Offset position) {
floatingButtonPosition.value = 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}) { static void show(bool isDarkMode, {Offset? anchor}) {
final controller = Get.find<TranslationController>(); final controller = Get.find<TranslationController>();
// 计算缩放对齐点(-1..1)。如果未提供 anchor,则从底部中间展开 // 总是从悬浮按钮位置展开,不依赖传入的anchor参数
Alignment alignment = Alignment.bottomCenter; final Size screenSize = MediaQuery.of(Get.context!).size;
if (anchor != null) { final Offset floatingButtonPos = controller.floatingButtonPosition.value;
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; final double buttonSize = 56.0; // 悬浮按钮的大小
alignment = Alignment(ax, ay); 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( Get.generalDialog(
barrierLabel: 'translation_mode_selector', barrierLabel: 'translation_mode_selector',
barrierDismissible: true, barrierDismissible: true, // 允许点击背景关闭
barrierColor: Colors.black.withOpacity(0.35), barrierColor: Colors.transparent, // 设置为透明,让自定义模糊层处理点击
transitionDuration: const Duration(milliseconds: 250), transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) { pageBuilder: (context, animation, secondaryAnimation) {
return const SizedBox.shrink(); return const SizedBox.shrink();
}, },
@ -35,17 +40,22 @@ class TranslationModeSelector {
reverseCurve: Curves.easeInCubic); reverseCurve: Curves.easeInCubic);
return Stack( return Stack(
children: [ children: [
// 模糊背景 // 修复:将模糊背景放在可点击区域
Positioned.fill( GestureDetector(
onTap: () => Get.back(), // 添加点击关闭功能
child: BackdropFilter( child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), 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( ScaleTransition(
alignment: alignment, 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( child: FadeTransition(
opacity: curved, opacity: curved,
child: SizedBox.expand( child: SizedBox.expand(
@ -127,19 +137,19 @@ class TranslationModeSelector {
} }
: null, : null,
), ),
SizedBox(height: 24.w), SizedBox(height: 64.w),
GestureDetector( // GestureDetector(
onTap: () => Get.back(), // onTap: () => Get.back(),
child: Container( // child: Container(
width: 42, // width: 42,
height: 42, // height: 42,
decoration: BoxDecoration( // decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.8), // color: Colors.black.withValues(alpha: 0.8),
borderRadius: BorderRadius.circular(50), // borderRadius: BorderRadius.circular(50),
), // ),
child: const Icon(Icons.close, color: Colors.white), // child: const Icon(Icons.close, color: Colors.white),
), // ),
), // ),
if (Platform.isAndroid) SizedBox(height: 20.h), 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: () { onTap: () {
// 点击标题时弹出翻译模式选择器 // 点击标题时弹出翻译模式选择器
// 从拖拽按钮位置作为锚点打开 // 从拖拽按钮位置作为锚点打开
final pos = controller.floatingButtonPosition.value; // final pos = controller.floatingButtonPosition.value;
final Size screen = MediaQuery.of(Get.context!).size; // final Size screen = MediaQuery.of(Get.context!).size;
// 已保存的是距右、下的偏移,需要换算为屏幕坐标中的中心点 // // 已保存的是距右、下的偏移,需要换算为屏幕坐标中的中心点
final double buttonSize = 56.w; // final double buttonSize = 56.w;
final double left = screen.width - pos.dx - buttonSize; // final double left = screen.width - pos.dx - buttonSize;
final double top = screen.height - pos.dy - buttonSize; // final double top = screen.height - pos.dy - buttonSize;
final Offset anchor = // final Offset anchor =
Offset(left + buttonSize / 2, top + buttonSize / 2); // Offset(left + buttonSize / 2, top + buttonSize / 2);
TranslationModeSelector.show(isDarkMode, anchor: anchor); TranslationModeSelector.show(isDarkMode);
}, },
child: Text( child: Text(
controller.currentModeTitle.value.tr, 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) { void _showTranslationModeSelector(bool isDarkMode) {
// 使用保存的位置换算按钮中心点(与标题点击一致) // 直接调用模式选择器,它会自动从悬浮按钮位置展开
final pos = controller.floatingButtonPosition.value; // 距右、下的偏移 TranslationModeSelector.show(isDarkMode);
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);
} }
} }

Loading…
Cancel
Save