hi-client/lib/app/services/global_overlay_service.dart

219 lines
7.6 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../modules/kr_home/views/hi_subscription_corner_button.dart';
import 'package:kaer_with_panels/main.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart';
class GlobalOverlayService extends GetxService {
Color? _currentColor;
static GlobalOverlayService get instance => Get.find<GlobalOverlayService>();
VoidCallback? _subscriptionAnimationTrigger;
void registerAnimationTrigger(VoidCallback trigger) {
_subscriptionAnimationTrigger = trigger;
}
/// 触发按钮动画跳转购买页
void triggerSubscriptionAnimation() {
if (_subscriptionAnimationTrigger != null) {
_subscriptionAnimationTrigger!.call();
} else {
print('🔥 Subscription animation trigger not registered yet');
}
}
OverlayEntry? _overlayEntry;
final RxBool _isVisible = false.obs;
final RxString _currentRoute = ''.obs;
bool get isVisible => _isVisible.value;
void updateCurrentRoute(String route) {
_currentRoute.value = route;
}
@override
void onInit() {
super.onInit();
print('🔥 GlobalOverlayService: Service initialized');
}
void showSubscriptionButton([OverlayState? overlay]) {
if (_isVisible.value) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
_doShowSubscriptionButton(overlay);
});
}
void _doShowSubscriptionButton([OverlayState? overlay, Color? color]) {
print('🔥 _doShowSubscriptionButton called, overlay=$overlay');
if (_isVisible.value) return;
try {
overlay ??= navigatorKey.currentState?.overlay;
if (overlay == null) {
print('🔥 GlobalOverlayService: No overlay found, cannot show subscription button');
return;
}
_overlayEntry = OverlayEntry(
builder: (context) {
final statusBarHeight = MediaQuery.of(context).padding.top < 62 ? 62 : MediaQuery.of(context).padding.top;
final double radius = 40.0 + statusBarHeight;
final double diameter = radius * 2;
return Stack(
children: [
// 1⃣ 圆背景,保持原来位置
Positioned(
top: -(diameter - (radius + statusBarHeight)),
right: -(radius - (statusBarHeight / 2)),
width: diameter,
height: diameter,
child: HISubscriptionCornerButton(
size: diameter,
color: _currentColor ?? Theme.of(context).primaryColor,
topOffset: -radius,
rightOffset: -radius,
),
),
// 2⃣ money-icon独立定位固定在屏幕右上角
Positioned(
top: MediaQuery.of(context).padding.top,
right: (radius / 2),
child: FractionalTranslation(
// Offset 的第一个参数是 x第二个是 y
// 0.5 代表向右偏移自身宽度的 50%-0.5 代表向左偏移自身宽度的 50%
translation: const Offset(0.5, 0),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
print('🔥 money-icon tapped');
GlobalOverlayService.instance.triggerSubscriptionAnimation();
},
child: IgnorePointer(
ignoring: false,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
KrLocalImage(
imageName: 'money-icon',
imageType: ImageType.svg,
width: 32.w, // 建议固定宽度以保证计算准确
),
SizedBox(height: 2.w),
Obx(() {
final current = _currentRoute.value;
print('🔥 GlobalOverlayService Obx: currentRoute = $current');
// ✅ 结合三种方式判断1. 显式设为透明 2. 路由名称匹配 3. 包含关键特征
bool isHidden = _currentColor == Colors.transparent ||
current == Routes.KR_PURCHASE_MEMBERSHIP ||
current.contains(Routes.KR_PURCHASE_MEMBERSHIP) ||
Routes.KR_PURCHASE_MEMBERSHIP.contains(current) ||
current == Routes.KR_ORDER_STATUS ||
current.contains('purchase-membership');
if (isHidden) {
return const SizedBox.shrink();
}
return DefaultTextStyle(
style: const TextStyle(
decoration: TextDecoration.none, // 核心:显式声明无装饰
),
child: Text(
'购买套餐',
style: TextStyle(
color: Colors.black,
fontSize: 12.sp,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none, // 也可以在这里直接写
),
),
);
}),
],
),
),
),
),
),
],
);
},
);
overlay.insert(_overlayEntry!);
_isVisible.value = true;
print('🔥 GlobalOverlayService: Subscription button shown successfully');
} catch (e) {
print('🔥 GlobalOverlayService: Error showing subscription button: $e');
}
}
void hideSubscriptionButton() {
if (!_isVisible.value) return;
_overlayEntry?.remove();
_overlayEntry = null;
_isVisible.value = false;
}
void toggleSubscriptionButton() {
if (_isVisible.value) {
hideSubscriptionButton();
} else {
showSubscriptionButton();
}
}
void safeShowSubscriptionButton() {
void _attemptShow() {
final overlay = navigatorKey.currentState?.overlay;
if (overlay == null) {
print('🔥 Overlay not ready, retrying...');
Future.delayed(const Duration(milliseconds: 200), _attemptShow);
return;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
print('🔥 Overlay ready, inserting subscription button');
showSubscriptionButton(overlay);
});
}
_attemptShow();
}
void updateSubscriptionButtonColor(Color? color) {
if (_overlayEntry == null) {
print('🔥 updateSubscriptionButtonColor: OverlayEntry is null');
return;
}
_currentColor = color;
_overlayEntry!.markNeedsBuild(); // 🔥 强制刷新 UI
}
void forceShowSubscriptionButton() {
print('🔥 GlobalOverlayService: Force showing subscription button');
hideSubscriptionButton();
Future.delayed(const Duration(milliseconds: 100), () {
safeShowSubscriptionButton();
});
}
@override
void onClose() {
hideSubscriptionButton();
super.onClose();
}
}