Flutter Tutorial

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

EffectDoes
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 / toTransparency
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.

Practise in the playground Updated