import 'package:flutter/material.dart' hide ConnectionState; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'dart:math' as math; import 'package:get/get.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; // 保存前一个渐变色用于动画过渡 void _onTap() { // 切换位置 setState(() { _isAtTop = !_isAtTop; }); 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; } @override Widget build(BuildContext context) { return GestureDetector(onTap: _onTap, child: _buildMainButton()); } Widget _buildMainButton() { // 根据状态和主题获取颜色 List gradientColor; String svgPath; String text; Color textColor; switch (widget.state) { case ConnectionState.disconnected: gradientColor = [ Get.reactiveTheme.highlightColor, Get.reactiveTheme.hintColor, ]; svgPath = Assets.switchStatusDisconnectedLight; text = "Disconnected"; textColor = Get.reactiveTheme.hintColor; if (_isAtTop) { _isAtTop = false; } break; case ConnectionState.connecting: gradientColor = [ Get.reactiveTheme.highlightColor, Get.reactiveTheme.hintColor, ]; svgPath = Assets.switchStatusConnectingLight; text = "Connecting"; textColor = Get.reactiveTheme.hintColor; break; case ConnectionState.connected: gradientColor = [ Get.reactiveTheme.shadowColor, Get.reactiveTheme.primaryColor, ]; svgPath = Assets.switchStatusConnectedLight; text = "Connected"; textColor = Get.reactiveTheme.textTheme.bodyLarge!.color!; if (!_isAtTop) { _isAtTop = true; } break; case ConnectionState.disconnecting: gradientColor = [ Get.reactiveTheme.highlightColor, Get.reactiveTheme.hintColor, ]; svgPath = Assets.switchStatusConnectingLight; text = "Disconnecting"; textColor = Get.reactiveTheme.hintColor; break; case ConnectionState.error: gradientColor = [ Get.reactiveTheme.highlightColor, Get.reactiveTheme.hintColor, ]; svgPath = Assets.switchStatusDisconnectedLight; 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: SvgPicture.asset( svgPath, key: ValueKey(svgPath), alignment: Alignment.center, width: 30.w, height: 30.w, ), ), ), ), ), 20.verticalSpaceFromWidth, Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SvgPicture.asset( Assets.switchStatusDisconnectedLight, 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; }); } }