gradient_switch.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. class GradientSwitch extends StatefulWidget {
  4. final bool value;
  5. final Future<bool> Function(bool)? onChanged;
  6. final double width;
  7. final double height;
  8. final Duration animationDuration;
  9. final double indicatorSize;
  10. final LinearGradient selectedGradient;
  11. final LinearGradient unselectedGradient;
  12. final Color? loadingColor;
  13. const GradientSwitch({
  14. super.key,
  15. required this.value,
  16. this.onChanged,
  17. this.width = 60.0,
  18. this.height = 30.0,
  19. this.animationDuration = const Duration(milliseconds: 300),
  20. this.indicatorSize = 24.0,
  21. this.selectedGradient =
  22. const LinearGradient(colors: [Colors.blue, Colors.purpleAccent]),
  23. this.unselectedGradient =
  24. const LinearGradient(colors: [Color(0xFFEEEEEE), Color(0xFFBDBDBD)]),
  25. this.loadingColor,
  26. });
  27. @override
  28. State<GradientSwitch> createState() => _GradientSwitchState();
  29. }
  30. class _GradientSwitchState extends State<GradientSwitch>
  31. with SingleTickerProviderStateMixin {
  32. late bool _currentValue;
  33. bool _isLoading = false;
  34. @override
  35. void initState() {
  36. super.initState();
  37. _currentValue = widget.value;
  38. }
  39. @override
  40. void didUpdateWidget(GradientSwitch oldWidget) {
  41. super.didUpdateWidget(oldWidget);
  42. if (widget.value != _currentValue && !_isLoading) {
  43. _currentValue = widget.value;
  44. }
  45. }
  46. Future<void> _handleTap() async {
  47. if (_isLoading || widget.onChanged == null) return;
  48. setState(() => _isLoading = true);
  49. final targetValue = !_currentValue;
  50. bool? success;
  51. try {
  52. success = await widget.onChanged!(targetValue);
  53. } finally {
  54. setState(() => _isLoading = false);
  55. if (success != null && mounted) {
  56. setState(() => _currentValue = success!);
  57. }
  58. }
  59. }
  60. @override
  61. void dispose() {
  62. super.dispose();
  63. }
  64. @override
  65. Widget build(BuildContext context) {
  66. return GestureDetector(
  67. onTap: _handleTap,
  68. child: AnimatedContainer(
  69. duration: widget.animationDuration,
  70. width: widget.width,
  71. height: widget.height,
  72. decoration: BoxDecoration(
  73. borderRadius: BorderRadius.circular(widget.height / 2),
  74. gradient: _currentValue
  75. ? widget.selectedGradient
  76. : widget.unselectedGradient,
  77. boxShadow: [
  78. BoxShadow(
  79. color: Colors.black.withOpacity(0.1),
  80. blurRadius: 4,
  81. offset: const Offset(0, 2),
  82. )
  83. ],
  84. ),
  85. child: AnimatedAlign(
  86. duration: widget.animationDuration,
  87. alignment:
  88. _currentValue ? Alignment.centerRight : Alignment.centerLeft,
  89. child: Container(
  90. margin: const EdgeInsets.symmetric(horizontal: 3),
  91. width: widget.indicatorSize,
  92. height: widget.indicatorSize,
  93. decoration: BoxDecoration(
  94. shape: BoxShape.circle,
  95. color: Colors.white,
  96. boxShadow: [
  97. BoxShadow(
  98. color: Colors.black.withOpacity(0.2),
  99. blurRadius: 4,
  100. offset: const Offset(0, 2),
  101. )
  102. ],
  103. ),
  104. child: _isLoading
  105. ? Center(
  106. child: CupertinoActivityIndicator(
  107. color: widget.loadingColor,
  108. radius: widget.indicatorSize * 0.3,
  109. ),
  110. )
  111. : null, // 非加载状态不显示内容
  112. ),
  113. ),
  114. ),
  115. );
  116. }
  117. }