import 'dart:math'; import 'package:flutter/material.dart'; class ConfettiOverlay extends StatefulWidget { const ConfettiOverlay({super.key}); @override State createState() => _ConfettiOverlayState(); } class _ConfettiOverlayState extends State with SingleTickerProviderStateMixin { late AnimationController _ctrl; final _rand = Random(); late List<_Particle> _particles; @override void initState() { super.initState(); _particles = List.generate(40, (_) => _Particle(_rand)); _ctrl = AnimationController( vsync: this, duration: const Duration(milliseconds: 2400), )..forward(); } @override void dispose() { _ctrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _ctrl, builder: (_, __) => CustomPaint( painter: _ConfettiPainter(_particles, _ctrl.value), size: Size.infinite, ), ); } } class _Particle { final double x; double y; final double speed; final double size; final Color color; final double rotation; final double rotSpeed; _Particle(Random r) : x = r.nextDouble(), y = -0.1 - r.nextDouble() * 0.3, speed = 0.3 + r.nextDouble() * 0.5, size = 6 + r.nextDouble() * 8, color = [ const Color(0xFF5B4FCF), const Color(0xFFFF7EB3), const Color(0xFFFFD700), const Color(0xFF4CAF50), const Color(0xFF1CB0F6), const Color(0xFFFF7043), ][r.nextInt(6)], rotation = r.nextDouble() * 2 * pi, rotSpeed = (r.nextDouble() - 0.5) * 6; } class _ConfettiPainter extends CustomPainter { final List<_Particle> particles; final double t; _ConfettiPainter(this.particles, this.t); @override void paint(Canvas canvas, Size size) { for (final p in particles) { final y = (p.y + t * p.speed) * size.height; if (y > size.height) continue; final x = p.x * size.width; final rot = p.rotation + t * p.rotSpeed; canvas.save(); canvas.translate(x, y); canvas.rotate(rot); final paint = Paint()..color = p.color; canvas.drawRect( Rect.fromCenter(center: Offset.zero, width: p.size, height: p.size * 0.5), paint, ); canvas.restore(); } } @override bool shouldRepaint(_ConfettiPainter old) => old.t != t; }