Created
June 20, 2024 13:32
-
-
Save stevenosse/d5cba3657d387cca04b22af1f2c0af45 to your computer and use it in GitHub Desktop.
Confetti experiment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'dart:math'; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
home: Scaffold( | |
body: Center( | |
child: ConfettiWidget( | |
numberOfParticles: 300, | |
particleTemplates: [ | |
Icon(Icons.star, color: Colors.red, size: 10), | |
Icon(Icons.favorite, color: Colors.pink, size: 10), | |
Icon(Icons.circle, color: Colors.blue, size: 10), | |
Icon(Icons.square, color: Colors.green, size: 10), | |
], | |
minSize: 5.0, | |
maxSize: 15.0, | |
duration: Duration(seconds: 5), | |
gravity: 0.05, | |
), | |
), | |
), | |
); | |
} | |
} | |
class ConfettiWidget extends StatefulWidget { | |
final int numberOfParticles; | |
final List<Widget> particleTemplates; | |
final double minSize; | |
final double maxSize; | |
final Duration duration; | |
final double gravity; | |
ConfettiWidget({ | |
this.numberOfParticles = 100, | |
required this.particleTemplates, | |
this.minSize = 5.0, | |
this.maxSize = 20.0, | |
this.duration = const Duration(seconds: 3), | |
this.gravity = 0.1, | |
}) : assert(particleTemplates.isNotEmpty); | |
@override | |
_ConfettiWidgetState createState() => _ConfettiWidgetState(); | |
} | |
class _ConfettiWidgetState extends State<ConfettiWidget> with SingleTickerProviderStateMixin { | |
late AnimationController _controller; | |
List<ConfettiParticle> _particles = []; | |
@override | |
void initState() { | |
super.initState(); | |
_controller = AnimationController( | |
duration: widget.duration, | |
vsync: this, | |
)..addListener(() { | |
setState(() { | |
_updateParticles(); | |
_generateParticles(); | |
}); | |
})..repeat(); | |
} | |
void _generateParticles() { | |
if (_particles.length < widget.numberOfParticles) { | |
_particles.addAll(List.generate(widget.numberOfParticles - _particles.length, (index) { | |
final isLeft = index % 2 == 0; | |
return ConfettiParticle( | |
widget: widget.particleTemplates[Random().nextInt(widget.particleTemplates.length)], | |
size: Random().nextDouble() * (widget.maxSize - widget.minSize) + widget.minSize, | |
x: isLeft ? Random().nextDouble() * MediaQuery.of(context).size.width / 2 : MediaQuery.of(context).size.width / 2 + Random().nextDouble() * MediaQuery.of(context).size.width / 2, | |
y: MediaQuery.of(context).size.height, | |
speedX: Random().nextDouble() * 4 - 2, | |
speedY: -Random().nextDouble() * 6 - 2, | |
gravity: widget.gravity, | |
); | |
})); | |
} | |
} | |
void _updateParticles() { | |
for (var particle in _particles) { | |
particle.update(); | |
if (particle.y < -20 || particle.x > MediaQuery.of(context).size.width || particle.x < 0) { | |
particle.resetPosition(MediaQuery.of(context).size.width, MediaQuery.of(context).size.height); | |
} | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Stack( | |
children: _particles.map((particle) { | |
return Positioned( | |
left: particle.x, | |
top: particle.y, | |
child: Transform.scale( | |
scale: particle.size / 20.0, // Normalize size for demonstration | |
child: particle.widget, | |
), | |
); | |
}).toList(), | |
); | |
} | |
@override | |
void dispose() { | |
_controller.dispose(); | |
super.dispose(); | |
} | |
} | |
class ConfettiParticle { | |
Widget widget; | |
double size; | |
double x; | |
double y; | |
double speedX; | |
double speedY; | |
double gravity; | |
ConfettiParticle({ | |
required this.widget, | |
required this.size, | |
required this.x, | |
required this.y, | |
required this.speedX, | |
required this.speedY, | |
required this.gravity, | |
}); | |
void update() { | |
x += speedX; | |
y += speedY; | |
speedY += gravity; | |
} | |
void resetPosition(double width, double height) { | |
final isLeft = Random().nextBool(); | |
x = isLeft ? Random().nextDouble() * width / 2 : width / 2 + Random().nextDouble() * width / 2; | |
y = height; | |
speedX = Random().nextDouble() * 4 - 2; | |
speedY = -Random().nextDouble() * 6 - 2; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment