feat: 增加动画变量,处理多个变量控制动画逻辑

This commit is contained in:
2025-12-01 19:41:38 -08:00
parent 86687cac58
commit aa0fd94cb2
2 changed files with 176 additions and 144 deletions
@@ -22,113 +22,96 @@ class HIAnimatedConnectButton extends GetView<KRHomeController> {
@override
Widget build(BuildContext context) {
return Obx(() {
final delay = controller.kr_currentNodeLatency.value;
final _ = KRSingBoxImp.instance.kr_status.value;
// 🔧 关键: 强制读取两个 observable 确保追踪
final _ = KRSingBoxImp.instance.kr_status.value; // 强制追踪
final isConnected = controller.kr_isConnected.value; // 使用 controller 的状态
// 再次读取状态用于判断
final status = KRSingBoxImp.instance.kr_status.value;
final isSwitching = status is SingboxStarting || status is SingboxStopping;
print('🔵 Switch UI 更新: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching');
final isShow = delay == -1 || isConnected;
final Color buttonColor = Theme.of(context).primaryColor;
final double screenWidth = Get.width;
final double screenHeight = Get.height;
final double buttonSize = 340;
final double currentButtonSize = isShow ? 270 : buttonSize;
final double currentButtonSize = 270;
return Stack(
children: [
AnimatedPositioned(
duration: const Duration(milliseconds: 400),
curve: Curves.linear,
left: isShow ? (screenWidth - currentButtonSize) / 2 : -60,
bottom: isShow ? screenHeight * 0.2 : -40,
left: (screenWidth - currentButtonSize) / 2,
bottom: screenHeight * 0.2,
onEnd: () {
controller.kr_onPositionAnimationEnd();
},
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
/// ✅ 呼吸同心圆背景
if (isShow)
if (controller.kr_isPositionAnimating.value)
Positioned.fill(
child: OverflowBox(
maxWidth: buttonSize * 3,
maxHeight: buttonSize * 3,
child: ContinuousRippleEffect(
color: Theme.of(context).primaryColor
),
color: Theme.of(context).primaryColor),
),
),
if (!isShow)
Positioned(
top: -20, // 距离顶部 10.w
left: 115, // 距离右侧 10.w
child: KrLocalImage(
imageName: "hi-home-slogan",
imageType: ImageType.svg,
),
),
/// ✅ 按钮主体
Material(
shape: const CircleBorder(),
color: isShow ? Colors.transparent : Theme.of(context).primaryColor,
color: Colors.transparent,
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () {
if(isSwitching) {
print('🔵 Switch UI 正在更新,切换中点击了按钮: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching');
return;
}
final current = controller.kr_subscribeService.kr_currentSubscribe.value;
bool hasValidSubscription = false;
if (current != null) {
DateTime? expire;
try {
expire = DateTime.parse(current.expireTime);
} catch (_) {
expire = null;
child: AbsorbPointer(
absorbing: controller.kr_isPositionAnimating.value,
child: InkWell(
onTap: () {
if (!controller.kr_canProcessConnectClick()) {
return;
}
hasValidSubscription = expire != null && expire.isAfter(DateTime.now());
}
if (hasValidSubscription) {
controller.kr_toggleSwitch(!controller.kr_isConnected.value);
} else {
HIDialog.show(
customMessageWidget: Padding(
padding: EdgeInsets.only(top: 16.w),
child: Text(
'尚未购买套餐,请前往购买页面下单后即可开始极速网络体验',
textAlign: TextAlign.left,
style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 14.sp,
fontWeight: FontWeight.w600,
final current = controller
.kr_subscribeService.kr_currentSubscribe.value;
bool hasValidSubscription = false;
if (current != null) {
DateTime? expire;
try {
expire = DateTime.parse(current.expireTime);
} catch (_) {
expire = null;
}
hasValidSubscription =
expire != null && expire.isAfter(DateTime.now());
}
if (hasValidSubscription) {
controller.kr_toggleSwitch(
!controller.kr_isConnected.value);
} else {
HIDialog.show(
customMessageWidget: Padding(
padding: EdgeInsets.only(top: 16.w),
child: Text(
'尚未购买套餐,请前往购买页面下单后即可开始极速网络体验',
textAlign: TextAlign.left,
style: KrAppTextStyle(
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
fontSize: 14.sp,
fontWeight: FontWeight.w600,
),
),
),
),
cancelText: '取消',
confirmText: '前往',
onConfirm: () {
GlobalOverlayService.instance.triggerSubscriptionAnimation();
},
);
}
},
child: SizedBox(
width: currentButtonSize,
height: currentButtonSize,
child: Stack(
alignment: Alignment.center,
children: [
if (isShow)
/// ✅ isShow = true,图片居中,文字贴近底部
cancelText: '取消',
confirmText: '前往',
onConfirm: () {
GlobalOverlayService.instance
.triggerSubscriptionAnimation();
},
);
}
},
child: SizedBox(
width: currentButtonSize,
height: currentButtonSize,
child: Stack(
alignment: Alignment.center,
children: [
Stack(
alignment: Alignment.center,
children: [
@@ -148,52 +131,25 @@ class HIAnimatedConnectButton extends GetView<KRHomeController> {
controller.kr_connectText.value == '已连接'
? '已连接\n点击断开'
: controller.kr_connectText.value,
key: ValueKey(controller.kr_connectText.value),
key: ValueKey(
controller.kr_connectText.value),
textAlign: TextAlign.center,
style: TextStyle(
style: const TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
height: 1
height: 1,
),
),
),
],
)
else
/// ✅ isShow = false,恢复原本布局
Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
KrLocalImage(
imageName: "hi-home-logo",
width: 104,
imageType: ImageType.svg,
),
SizedBox(height: 2.h),
Text(
controller.kr_connectText.value == '已连接'
? '已连接\n点击断开'
: controller.kr_connectText.value,
key: ValueKey(controller.kr_connectText.value),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
],
),
],
],
),
),
),
),
),
],
),
),
@@ -202,6 +158,7 @@ class HIAnimatedConnectButton extends GetView<KRHomeController> {
});
}
}
/// ✅ 呼吸式同心圆动画(最小圆固定270,其他圆在270~479间波动,初始直径差10%)
/// ✅ 呼吸式同心圆动画(所有圆在270~470之间运动,起点相同但周期不同)
class ContinuousRippleEffect extends StatefulWidget {
@@ -232,10 +189,11 @@ class _ContinuousRippleEffectState extends State<ContinuousRippleEffect>
// 动画周期差异:外层慢,内层快
final duration = Duration(milliseconds: 3000 + i * 1200);
final controller =
AnimationController(vsync: this, duration: duration)..repeat(reverse: true);
final controller = AnimationController(vsync: this, duration: duration)
..repeat(reverse: true);
final curved = CurvedAnimation(parent: controller, curve: Curves.easeInOutSine);
final curved =
CurvedAnimation(parent: controller, curve: Curves.easeInOutSine);
_controllers.add(controller);
_animations.add(curved);
}