import 'package:flutter/material.dart' hide ConnectionState; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'dart:math' as math; 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 '../../../constants/enums.dart'; class ConnectionButton extends StatefulWidget { final ConnectionState state; final VoidCallback? onTap; const ConnectionButton({super.key, required this.state, this.onTap}); @override State createState() => _ConnectionButtonState(); } class _ConnectionButtonState extends State with TickerProviderStateMixin { bool _isAtTop = false; // 控制按钮位置,false=底部,true=顶部 List? _previousGradientColor; // 保存前一个渐变色用于动画过渡 late AnimationController _rotationController; // 旋转动画控制器 @override void initState() { super.initState(); // 初始化旋转动画控制器 _rotationController = AnimationController( duration: const Duration(seconds: 2), vsync: this, ); } @override void dispose() { _rotationController.dispose(); super.dispose(); } void _onTap() { if (widget.onTap != null) { widget.onTap!(); } } // 比较两个颜色列表是否相等 bool _colorsEqual(List a, List b) { if (a.length != b.length) return false; for (int i = 0; i < a.length; i++) { if (a[i].value != b[i].value) return false; } return true; } Widget _buildImageWithAnimation(String imgPath) { // 如果是connecting状态,添加旋转动画 if (widget.state == ConnectionState.connecting) { // 启动旋转动画 if (!_rotationController.isAnimating) { _rotationController.repeat(); } return AnimatedBuilder( animation: _rotationController, builder: (context, child) { return Transform.rotate( angle: _rotationController.value * 2 * math.pi, child: IXImage( source: imgPath, sourceType: ImageSourceType.asset, width: 30.w, height: 30.w, ), ); }, ); } else { // 其他状态停止旋转动画 if (_rotationController.isAnimating) { _rotationController.stop(); } return IXImage( source: imgPath, sourceType: ImageSourceType.asset, width: 30.w, height: 30.w, ); } } @override Widget build(BuildContext context) { return GestureDetector(onTap: _onTap, child: _buildMainButton()); } Widget _buildMainButton() { // 根据状态和主题获取颜色 List gradientColor; String imgPath; String statusImgPath; String text; Color textColor; switch (widget.state) { case ConnectionState.disconnected: gradientColor = [ Get.reactiveTheme.highlightColor, Get.reactiveTheme.hintColor, ]; imgPath = Assets.switchStatusDisconnected; statusImgPath = Assets.disconnected; text = "Disconnected"; textColor = Get.reactiveTheme.hintColor; if (_isAtTop) { setState(() { _isAtTop = false; }); } break; case ConnectionState.connecting: gradientColor = [ Get.reactiveTheme.highlightColor, Get.reactiveTheme.hintColor, ]; imgPath = Assets.switchStatusConnecting; statusImgPath = Assets.connecting; text = "Connecting"; textColor = Get.reactiveTheme.hintColor; // 切换位置 setState(() { _isAtTop = !_isAtTop; }); break; case ConnectionState.connected: gradientColor = [ Get.reactiveTheme.shadowColor, Get.reactiveTheme.primaryColor, ]; imgPath = Assets.switchStatusConnected; statusImgPath = Assets.connected; text = "Connected"; textColor = Get.reactiveTheme.textTheme.bodyLarge!.color!; if (!_isAtTop) { setState(() { _isAtTop = true; }); } break; case ConnectionState.error: gradientColor = [ Get.reactiveTheme.highlightColor, Get.reactiveTheme.hintColor, ]; imgPath = Assets.switchStatusDisconnected; statusImgPath = Assets.error; text = "Error"; textColor = Get.reactiveTheme.hintColor; break; } // 保存前一个渐变色用于动画 final previousColor = _previousGradientColor ?? gradientColor; // 只在颜色真正改变时更新 if (_previousGradientColor == null || _previousGradientColor!.length != gradientColor.length || !_colorsEqual(_previousGradientColor!, gradientColor)) { // 延迟更新,避免在 build 中调用 setState WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _previousGradientColor = gradientColor; } }); } return Column( children: [ TweenAnimationBuilder>( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, tween: ColorListTween(begin: previousColor, end: gradientColor), builder: (context, colors, child) { return Container( width: 72.w, height: 132.w, padding: EdgeInsets.all(6.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20.r), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: colors, ), ), child: child, ); }, child: AnimatedAlign( duration: const Duration(milliseconds: 300), curve: Curves.fastOutSlowIn, alignment: _isAtTop ? Alignment.topCenter : Alignment.bottomCenter, child: Container( width: 60.w, height: 60.w, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(14.r), ), child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: ScaleTransition( scale: Tween( begin: 0.85, end: 1.0, ).animate(animation), child: child, ), ); }, child: _buildImageWithAnimation(imgPath), ), ), ), ), 20.verticalSpaceFromWidth, Row( mainAxisAlignment: MainAxisAlignment.center, 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, color: textColor, ), ), ], ), ], ); } } // 自定义颜色列表插值器,用于渐变色动画 class ColorListTween extends Tween> { ColorListTween({super.begin, super.end}); @override List lerp(double t) { if (begin == null || end == null) { return end ?? begin ?? []; } final length = math.max(begin!.length, end!.length); return List.generate(length, (index) { final beginColor = index < begin!.length ? begin![index] : begin!.last; final endColor = index < end!.length ? end![index] : end!.last; return Color.lerp(beginColor, endColor, t) ?? endColor; }); } }