import 'package:flutter/material.dart' hide ConnectionState; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'dart:math' as math; import 'dart:async'; import 'package:get/get.dart'; import 'package:nomo/app/widgets/ix_image.dart'; import '../../../constants/assets.dart'; import '../../../../config/theme/theme_extensions/theme_extension.dart'; import '../../../../config/translations/strings_enum.dart'; import '../../../constants/enums.dart'; class ConnectionRoundButton extends StatefulWidget { final ConnectionState state; final VoidCallback? onTap; const ConnectionRoundButton({super.key, required this.state, this.onTap}); @override State createState() => _ConnectionRoundButtonState(); } class _ConnectionRoundButtonState extends State with TickerProviderStateMixin { late AnimationController _rotationController; // 旋转动画控制器 late AnimationController _fadeController; // 淡入淡出控制器 Timer? _connectingTimer; // 连接中状态的计时器 int _connectingTextIndex = 0; // 当前显示的连接文本索引(0-4) ConnectionState? _previousState; // 保存前一个连接状态 bool _isStoppingRotation = false; // 是否正在停止旋转 @override void initState() { super.initState(); // 初始化旋转动画控制器 _rotationController = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); // 初始化淡入淡出控制器 _fadeController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, value: 1.0, ); // 如果初始状态是 connecting,启动动画 if (widget.state == ConnectionState.connecting) { _rotationController.repeat(); _startConnectingTimer(); } } @override void didUpdateWidget(ConnectionRoundButton oldWidget) { super.didUpdateWidget(oldWidget); // 处理状态变化 if (oldWidget.state != widget.state) { _handleStateChange(oldWidget.state); } } void _handleStateChange(ConnectionState oldState) { if (widget.state == ConnectionState.connecting) { _isStoppingRotation = false; if (!_rotationController.isAnimating) { _rotationController.repeat(); } if (_connectingTimer == null || !_connectingTimer!.isActive) { _startConnectingTimer(); } } else { // 从连接中切换到其他状态时,平滑停止旋转 if (_rotationController.isAnimating && !_isStoppingRotation) { _isStoppingRotation = true; // 计算剩余角度,让动画平滑停止在顶部(0度位置) final currentValue = _rotationController.value; // 停止重复,然后平滑减速到完整的一圈 _rotationController.stop(); _rotationController .animateTo( 1.0, duration: Duration( milliseconds: ((1.0 - currentValue) * 400).toInt().clamp( 100, 400, ), ), curve: Curves.easeOutCubic, ) .then((_) { if (mounted) { _rotationController.reset(); _isStoppingRotation = false; } }); } _stopConnectingTimer(); } } @override void dispose() { _rotationController.dispose(); _fadeController.dispose(); _connectingTimer?.cancel(); super.dispose(); } void _onTap() { if (widget.onTap != null) { widget.onTap!(); } } // 启动连接中状态的文本轮播计时器 void _startConnectingTimer() { _connectingTimer?.cancel(); _connectingTextIndex = 0; _connectingTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (mounted) { setState(() { // 每秒切换到下一个文本,循环显示0-4 _connectingTextIndex = (_connectingTextIndex + 1) % 5; }); } }); } // 停止连接中状态的文本轮播计时器 void _stopConnectingTimer() { _connectingTimer?.cancel(); _connectingTimer = null; _connectingTextIndex = 0; } // 根据索引获取对应的连接文本 String _getConnectingText() { switch (_connectingTextIndex) { case 0: return Strings.securingData.tr; case 1: return Strings.encryptingTraffic.tr; case 2: return Strings.protectingPrivacy.tr; case 3: return Strings.safeConnection.tr; case 4: return Strings.yourDataIsSafe.tr; default: return Strings.connecting.tr; } } // 构建旋转的圆环 Widget _buildRoundRing(String svgPath, bool shouldRotate) { final ringWidget = IXImage( key: ValueKey('ring_$svgPath'), source: svgPath, sourceType: ImageSourceType.asset, width: 170.w, height: 170.w, ); if (shouldRotate) { return AnimatedBuilder( key: const ValueKey('rotating_ring'), animation: _rotationController, builder: (context, child) { return Transform.rotate( angle: _rotationController.value * 2 * math.pi, child: child, ); }, child: ringWidget, ); } return ringWidget; } // 构建电源图标 Widget _buildPowerIcon(bool isConnected) { return IXImage( key: ValueKey('power_icon_$isConnected'), source: isConnected ? Assets.connectedSwitch : Assets.disconnectedSwitch, sourceType: ImageSourceType.asset, width: 48.w, height: 48.w, ); } @override Widget build(BuildContext context) { return GestureDetector(onTap: _onTap, child: _buildMainButton()); } Widget _buildMainButton() { // 根据状态获取对应的资源和样式 String ringPath; String statusImgPath; String text; Color textColor; bool isConnected; bool shouldRotate; switch (widget.state) { case ConnectionState.disconnected: ringPath = Assets.disconnectedRound; statusImgPath = Assets.disconnected; text = Strings.disconnected.tr; textColor = Get.reactiveTheme.hintColor; isConnected = false; shouldRotate = false; break; case ConnectionState.connecting: ringPath = Assets.connectingRound; statusImgPath = Assets.connecting; text = _getConnectingText(); // 使用轮播文本 textColor = Get.reactiveTheme.hintColor; isConnected = false; shouldRotate = true; break; case ConnectionState.connected: ringPath = Assets.connectedRound; statusImgPath = Assets.connected; text = Strings.connected.tr; textColor = Get.reactiveTheme.textTheme.bodyLarge!.color!; isConnected = true; shouldRotate = false; break; case ConnectionState.error: ringPath = Assets.disconnectedRound; statusImgPath = Assets.error; text = Strings.error.tr; textColor = Get.reactiveTheme.hintColor; isConnected = false; shouldRotate = false; break; } // 更新前一个状态 if (_previousState != widget.state) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _previousState = widget.state; } }); } return Column( mainAxisSize: MainAxisSize.min, children: [ // 圆形按钮区域 SizedBox( width: 170.w, height: 170.w, child: Stack( alignment: Alignment.center, children: [ // 圆环背景 - 使用纯淡入淡出,无缩放,更自然 AnimatedSwitcher( duration: const Duration(milliseconds: 600), switchInCurve: Curves.easeInOut, switchOutCurve: Curves.easeInOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: CurvedAnimation( parent: animation, curve: Curves.easeInOut, ), child: child, ); }, layoutBuilder: (currentChild, previousChildren) { return Stack( alignment: Alignment.center, children: [ ...previousChildren, if (currentChild != null) currentChild, ], ); }, child: _buildRoundRing(ringPath, shouldRotate), ), // 电源图标 - 纯淡入淡出 AnimatedSwitcher( duration: const Duration(milliseconds: 500), switchInCurve: Curves.easeInOut, switchOutCurve: Curves.easeInOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: CurvedAnimation( parent: animation, curve: Curves.easeInOut, ), child: child, ); }, layoutBuilder: (currentChild, previousChildren) { return Stack( alignment: Alignment.center, children: [ ...previousChildren, if (currentChild != null) currentChild, ], ); }, child: _buildPowerIcon(isConnected), ), ], ), ), 20.verticalSpaceFromWidth, // 状态文字 AnimatedSwitcher( duration: const Duration(milliseconds: 350), switchInCurve: Curves.easeOutCubic, switchOutCurve: Curves.easeInCubic, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: CurvedAnimation( parent: animation, curve: Curves.easeInOut, ), child: SlideTransition( position: Tween( begin: const Offset(0, 0.15), end: Offset.zero, ).animate( CurvedAnimation( parent: animation, curve: Curves.easeOutCubic, ), ), child: child, ), ); }, child: Row( key: ValueKey('status_$text'), // 使用文本作为 key,确保文字改变时触发动画 mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ IXImage( source: statusImgPath, sourceType: ImageSourceType.asset, width: 14.w, height: 14.w, ), 4.horizontalSpace, Text( text, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, height: 1.4, color: textColor, ), ), ], ), ), ], ); } }