import 'package:flutter/material.dart'; enum RippleShape { circle, rectangle, } class FikoYTRippleWave extends StatefulWidget { const FikoYTRippleWave({ super.key, this.color = Colors.yellowAccent, this.duration = const Duration(milliseconds: 1500), this.delay = Duration.zero, this.repeat = true, required this.child, this.childTween, this.animationController, this.waveCount = 2, this.shape = RippleShape.circle, this.rectRadius = 16.0, }); final Color color; final Widget child; final Tween? childTween; final Duration duration; final Duration delay; final bool repeat; final AnimationController? animationController; final int waveCount; final RippleShape shape; final double rectRadius; @override State createState() => FikoYTRippleWaveState(); } class FikoYTRippleWaveState extends State with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { late AnimationController _controller; @override bool get wantKeepAlive => true; @override void initState() { super.initState(); _initializeController(); } void _initializeController() { _controller = widget.animationController ?? AnimationController( duration: widget.duration, vsync: this, ); _controller.addStatusListener((status) { if (status == AnimationStatus.completed) { _controller.value = 0; Future.delayed(widget.delay, () { if (mounted && widget.repeat) { _controller.forward(from: 0.0); } }); } }); if (widget.repeat) { _controller.forward(); } else if (widget.animationController == null) { _controller.forward(); } } @override void didUpdateWidget(covariant FikoYTRippleWave oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.repeat != widget.repeat) { if (widget.repeat) { _controller.repeat(); } else { _controller.stop(); } } if (oldWidget.duration != widget.duration) { _controller.dispose(); _initializeController(); } } @override void dispose() { if (widget.animationController == null) { _controller.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { return CustomPaint( painter: _RipplePainter( _controller, widget.waveCount, color: widget.color, shape: widget.shape, rectRadius: widget.rectRadius, ), child: RepaintBoundary( child: Center( child: ClipRRect( borderRadius: BorderRadius.circular(100), child: DecoratedBox( decoration: BoxDecoration( gradient: RadialGradient( colors: [ widget.color, Colors.transparent, ], ), ), child: widget.child, ), ), ), ), ); } } class _RipplePainter extends CustomPainter { _RipplePainter( this.animation, this.waveCount, { required this.color, required this.shape, this.rectRadius = 16.0, }) : super(repaint: animation); final Color color; final Animation animation; final int waveCount; final RippleShape shape; final double rectRadius; void circle(Canvas canvas, Rect rect, double value) { final double maxRadius = rect.shortestSide / 2; // Daha tutarlı oran final double normalizedValue = value / waveCount; final double radius = maxRadius * normalizedValue; final double opacity = (1.0 - normalizedValue).clamp(0.0, 1.0); final Color newColor = color.withOpacity(opacity); final Paint paint = Paint()..color = newColor; canvas.drawCircle( rect.center, radius, paint, ); } void roundedRectangle(Canvas canvas, Rect rect, double value) { final double normalizedValue = value / waveCount; final double inset = (rect.shortestSide / 2) * (1.0 - normalizedValue); // Benzer ölçekleme final double opacity = (1.0 - normalizedValue).clamp(0.0, 1.0); final Color newColor = color.withOpacity(opacity); final Paint paint = Paint()..color = newColor; final RRect rRect = RRect.fromRectAndRadius( rect.deflate(inset), Radius.circular(rectRadius), ); canvas.drawRRect(rRect, paint); } @override void paint(Canvas canvas, Size size) { final Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height); for (int wave = 0; wave <= waveCount; wave++) { final double value = wave + animation.value; if (shape == RippleShape.circle) { circle(canvas, rect, value); } else if (shape == RippleShape.rectangle) { roundedRectangle(canvas, rect, value); } } } @override bool shouldRepaint(_RipplePainter oldDelegate) { return oldDelegate.animation != animation || oldDelegate.color != color || oldDelegate.waveCount != waveCount || oldDelegate.shape != shape || oldDelegate.rectRadius != rectRadius; } }