You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/web/particle_background/lib/main.dart

172 lines
4.4 KiB

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:particle_background/simple_animations_package.dart';
void main() => runApp(ParticleApp());
class ParticleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ParticleBackgroundPage(),
),
);
}
}
class ParticleBackgroundPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned.fill(child: AnimatedBackground()),
Positioned.fill(child: Particles(30)),
Positioned.fill(child: CenteredText()),
],
);
}
}
class Particles extends StatefulWidget {
final int numberOfParticles;
Particles(this.numberOfParticles);
@override
_ParticlesState createState() => _ParticlesState();
}
class _ParticlesState extends State<Particles> {
final Random random = Random();
final List<ParticleModel> particles = [];
@override
void initState() {
List.generate(widget.numberOfParticles, (index) {
particles.add(ParticleModel(random));
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Rendering(
startTime: Duration(seconds: 30),
onTick: _simulateParticles,
builder: (context, time) {
return CustomPaint(
painter: ParticlePainter(particles, time),
);
},
);
}
_simulateParticles(Duration time) {
particles.forEach((particle) => particle.maintainRestart(time));
}
}
class ParticleModel {
Animatable tween;
double size;
AnimationProgress animationProgress;
Random random;
ParticleModel(this.random) {
restart();
}
restart({Duration time = Duration.zero}) {
final startPosition = Offset(-0.2 + 1.4 * random.nextDouble(), 1.2);
final endPosition = Offset(-0.2 + 1.4 * random.nextDouble(), -0.2);
final duration = Duration(milliseconds: 3000 + random.nextInt(6000));
tween = MultiTrackTween([
Track("x").add(
duration, Tween(begin: startPosition.dx, end: endPosition.dx),
curve: Curves.easeInOutSine),
Track("y").add(
duration, Tween(begin: startPosition.dy, end: endPosition.dy),
curve: Curves.easeIn),
]);
animationProgress = AnimationProgress(duration: duration, startTime: time);
size = 0.2 + random.nextDouble() * 0.4;
}
maintainRestart(Duration time) {
if (animationProgress.progress(time) == 1.0) {
restart(time: time);
}
}
}
class ParticlePainter extends CustomPainter {
List<ParticleModel> particles;
Duration time;
ParticlePainter(this.particles, this.time);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white.withAlpha(50);
particles.forEach((particle) {
var progress = particle.animationProgress.progress(time);
final animation = particle.tween.transform(progress);
final position =
Offset(animation["x"] * size.width, animation["y"] * size.height);
canvas.drawCircle(position, size.width * 0.2 * particle.size, paint);
});
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
class AnimatedBackground extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tween = MultiTrackTween([
Track("color1").add(Duration(seconds: 3),
ColorTween(begin: Color(0xff8a113a), end: Colors.lightBlue.shade900)),
Track("color2").add(Duration(seconds: 3),
ColorTween(begin: Color(0xff440216), end: Colors.blue.shade600))
]);
return ControlledAnimation(
playback: Playback.MIRROR,
tween: tween,
duration: tween.duration,
builder: (context, animation) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [animation["color1"], animation["color2"]])),
);
},
);
}
}
class CenteredText extends StatelessWidget {
const CenteredText({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Text(
"Welcome to Flutter for web",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w200),
textScaleFactor: 4,
),
);
}
}