| 1 | import 'package:flutter/material.dart'; |
| 2 | |
| 3 | class PulsingDot extends StatefulWidget { |
| 4 | const PulsingDot({ |
| 5 | super.key, |
| 6 | }); |
| 7 | |
| 8 | final double size = 5; |
| 9 | final Color color = const Color(0xFFFFC414); |
| 10 | final Duration fadeOutDuration = const Duration(milliseconds: 2000); |
| 11 | final Duration restDuration = const Duration(milliseconds: 2000); |
| 12 | final double restOpacity = 0.3; |
| 13 | |
| 14 | @override |
| 15 | State<PulsingDot> createState() => _PulsingDotState(); |
| 16 | } |
| 17 | |
| 18 | class _PulsingDotState extends State<PulsingDot> with SingleTickerProviderStateMixin { |
| 19 | late final AnimationController controller; |
| 20 | |
| 21 | @override |
| 22 | void initState() { |
| 23 | super.initState(); |
| 24 | controller = AnimationController( |
| 25 | vsync: this, |
| 26 | duration: widget.fadeOutDuration, |
| 27 | reverseDuration: Duration.zero, |
| 28 | value: 1.0, |
| 29 | ); |
| 30 | _loop(); |
| 31 | } |
| 32 | |
| 33 | Future<void> _loop() async { |
| 34 | while (mounted) { |
| 35 | await controller.animateTo( |
| 36 | widget.restOpacity, |
| 37 | curve: Curves.easeOutQuad, |
| 38 | duration: widget.fadeOutDuration, |
| 39 | ); |
| 40 | await Future.delayed(widget.restDuration); |
| 41 | if (!mounted) return; |
| 42 | controller.value = 1.0; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | @override |
| 47 | void dispose() { |
| 48 | controller.dispose(); |
| 49 | super.dispose(); |
| 50 | } |
| 51 | |
| 52 | @override |
| 53 | Widget build(BuildContext context) { |
| 54 | return FadeTransition( |
| 55 | opacity: controller, |
| 56 | child: Container( |
| 57 | width: widget.size, |
| 58 | height: widget.size, |
| 59 | decoration: BoxDecoration( |
| 60 | color: widget.color, |
| 61 | shape: BoxShape.circle, |
| 62 | ), |
| 63 | ), |
| 64 | ); |
| 65 | } |
| 66 | } |