Flutter Intermediate Lesson 111 of 135 3 min read
Effects and Particles in Flame: Move, Scale, Fade and Explosions
Make Flutter games feel alive with Flame effects (move, scale, rotate, colour, fade, sequence) and particle explosions, in a runnable example.
On this page
Effects change a component smoothly over time: move it, grow it, spin it, fade it. Particles are many tiny short-lived objects: sparks, smoke, confetti, explosions. Together they give a game “juice”: the feeling that it reacts to everything you do.
Effects in one example
Tap the shapes.
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/events.dart';
import 'package:flame/game.dart';
import 'package:flame/particles.dart';
import 'package:flutter/material.dart';
void main() => runApp(GameWidget(game: JuiceGame()));
class JuiceGame extends FlameGame {
@override
Future<void> onLoad() async {
camera.viewfinder.anchor = Anchor.topLeft;
world.add(Bouncy(Vector2(size.x * 0.25, size.y / 2)));
world.add(Spinner(Vector2(size.x * 0.5, size.y / 2)));
world.add(Exploder(Vector2(size.x * 0.75, size.y / 2)));
world.add(TextComponent(text: 'Tap each shape', position: Vector2(16, 16)));
}
}
// Bounces forever, and jumps when tapped.
class Bouncy extends RectangleComponent with TapCallbacks {
Bouncy(Vector2 position)
: super(position: position, size: Vector2.all(60), anchor: Anchor.center,
paint: Paint()..color = Colors.lightBlueAccent);
@override
Future<void> onLoad() async {
add(MoveEffect.by(
Vector2(0, -30),
EffectController(duration: 0.5, reverseDuration: 0.5, infinite: true, curve: Curves.easeOut),
));
}
@override
void onTapDown(TapDownEvent event) {
add(SequenceEffect([
ScaleEffect.to(Vector2.all(1.4), EffectController(duration: 0.1)),
ScaleEffect.to(Vector2.all(1.0), EffectController(duration: 0.2)),
]));
add(ColorEffect(Colors.pink, EffectController(duration: 0.15, alternate: true)));
}
}
// Spins one full turn when tapped.
class Spinner extends RectangleComponent with TapCallbacks {
Spinner(Vector2 position)
: super(position: position, size: Vector2(80, 20), anchor: Anchor.center,
paint: Paint()..color = Colors.amber);
@override
void onTapDown(TapDownEvent event) {
add(RotateEffect.by(2 * pi, EffectController(duration: 0.6, curve: Curves.easeInOut)));
}
}
// Explodes into particles and fades away when tapped.
class Exploder extends CircleComponent with TapCallbacks {
Exploder(Vector2 position)
: super(position: position, radius: 30, anchor: Anchor.center,
paint: Paint()..color = Colors.deepOrange);
final _random = Random();
@override
void onTapDown(TapDownEvent event) {
parent?.add(ParticleSystemComponent(
position: position.clone(),
particle: Particle.generate(
count: 40,
lifespan: 0.8,
generator: (i) => AcceleratedParticle(
acceleration: Vector2(0, 300),
speed: (Vector2.random(_random) - Vector2.all(0.5)) * 500,
child: CircleParticle(
radius: 2 + _random.nextDouble() * 3,
paint: Paint()..color = Colors.primaries[i % Colors.primaries.length],
),
),
),
));
add(OpacityEffect.fadeOut(EffectController(duration: 0.3), onComplete: () {
// Come back after a moment
add(OpacityEffect.fadeIn(EffectController(duration: 0.3, startDelay: 0.7)));
}));
}
}
The effects you will use
| Effect | Does |
|---|---|
MoveEffect.by(offset, ctrl) / MoveEffect.to(point, ctrl) | Move |
ScaleEffect.by(factor, ctrl) / ScaleEffect.to(scale, ctrl) | Grow or shrink |
RotateEffect.by(angle, ctrl) / RotateEffect.to(angle, ctrl) | Spin |
ColorEffect(color, ctrl) | Tint |
OpacityEffect.fadeIn / fadeOut / to | Transparency |
SequenceEffect([...]) | One after another |
RemoveEffect(delay: 1) | Remove the component after a delay |
The EffectController
The controller says how the effect runs:
EffectController(
duration: 0.5, // seconds
reverseDuration: 0.5, // then play backwards
curve: Curves.easeOut, // speed curve
infinite: true, // forever
repeatCount: 3, // or a number of times
alternate: true, // go there and back
startDelay: 0.2, // wait first
)
Add onComplete: () { ... } to any effect to do something when it ends.
Particles
A particle system has:
- A generator that creates each particle (
Particle.generate(count: 40, generator: ...)). - Behaviour:
AcceleratedParticle(speed + gravity),MovingParticle(from A to B),RotatingParticle. - A look:
CircleParticle,ComputedParticle(draw anything),SpriteParticle(an image). - A lifespan in seconds, after which everything is removed automatically.
Common uses: coins sparkling, dust when landing, smoke trails, confetti on a win.
Try it yourself
Make the explosion particles shoot only upwards (like a fountain) by using Vector2(random x between -100 and 100, random y between -500 and -300) as the speed.