particles_flutter 3.0.2 copy "particles_flutter: ^3.0.2" to clipboard
particles_flutter: ^3.0.2 copied to clipboard

A flutter package for particle animation. You can control the animation speed, number of Particles on the screen, dimension and shape of particle via connecting lines,etc.

particles_flutter #

High-performance, fully customisable particle animations for Flutter. Starfields, snow, confetti, fireworks, connected webs, comets, burst explosions — with physics, touch/hover interaction, lifetime animations, trails, and burst emitters.

pub package pub points likes License: MIT Live Demo Buy Me A Coffee

Live Demo · pub.dev · Issues · Contributing


     


Table of Contents #


Install #

flutter pub add particles_flutter

Quick Start #

import 'package:particles_flutter/engine.dart';
import 'package:particles_flutter/shapes.dart';

Particles(
  width: MediaQuery.of(context).size.width,
  height: MediaQuery.of(context).size.height,
  boundType: BoundType.WrapAround,
  particles: List.generate(80, (_) {
    final rng = Random();
    return CircularParticle(
      radius: rng.nextDouble() * 4 + 1,
      color: Colors.white.withValues(alpha: 0.7),
      velocity: Offset(
        (rng.nextDouble() - 0.5) * 60,
        (rng.nextDouble() - 0.5) * 60,
      ),
    );
  }),
)

Features #

Particle Shapes #

Import: import 'package:particles_flutter/shapes.dart';

Shape Class
Circle CircularParticle(radius, color, velocity)
Rectangle RectangularParticle(width, height, color, velocity)
Rounded Rectangle RoundRectangularParticle(width, height, cornerRadius, ...)
Triangle TriangularParticle(width, height, color, velocity)
Oval OvoidalParticle(width, height, color, velocity, rotationSpeed)
Image ImageParticle(image, width, height, color, velocity)

All shapes support rotationSpeed and all lifetime animation parameters.


Lifetime Animations #

All shapes accept these optional parameters. Omit any to keep default behavior.

Color over lifetime

CircularParticle(
  color: Colors.yellow,
  lifetime: 3.0,
  // Two-color transition:
  endColor: Colors.transparent,
  // OR gradient across lifetime:
  colorGradient: [Colors.white, Colors.yellow, Colors.orange, Colors.transparent],
  colorCurve: Curves.linear,
  ...
)

Scale over lifetime

CircularParticle(
  color: Colors.purple,
  lifetime: 2.5,
  startScale: 0.1,
  endScale: 1.8,
  scaleCurve: Curves.easeInOut,
  ...
)

Fade over lifetime

CircularParticle(
  color: Colors.white,
  lifetime: 2.0,
  startOpacity: 1.0,
  endOpacity: 0.0,
  opacityCurve: Curves.easeIn,
  ...
)

Tip: Set both startOpacity: 0.0 and endOpacity: 0.0 for a triangle fade — particles fade in to full opacity at mid-life, then back out. No visible pop on spawn or death.

CircularParticle(
  lifetime: 3.0,
  startOpacity: 0.0,  // fade in from invisible
  endOpacity: 0.0,    // fade out to invisible — triangle curve auto-applied
  ...
)

Particle trails

CircularParticle(
  color: Colors.cyan,
  lifetime: 2.0,
  trailEnabled: true,
  trailLength: 7,   // past positions to draw
  trailFade: true,  // fade older segments
  ...
)

Boundary Types #

Value Behaviour
BoundType.None Particles exit the canvas (default)
BoundType.WrapAround Particles reappear from the opposite edge
BoundType.Bounce Particles reflect off edges
Particles(
  boundType: BoundType.WrapAround,
  ...
)

Touch & Hover Interaction #

Import: import 'package:particles_flutter/interactions.dart';

Particles(
  interaction: ParticleInteraction(
    awayRadius: 120,
    enableHover: true,
    onTapAnimation: true,
    awayAnimationDuration: Duration(milliseconds: 400),
    awayAnimationCurve: Curves.easeOut,
    hoverRadius: 80,
  ),
  ...
)

Particle Physics #

Import: import 'package:particles_flutter/physics.dart';

Particles(
  particlePhysics: ParticlePhysics(gravityScale: 30),
  ...
)

Emission Controller #

Spawn particles from a fixed point — great for fountains and fireworks.

Particles(
  boundType: BoundType.None,
  particleEmitter: Emitter(
    startPosition: Offset(width / 2, height / 2),
    startPositionRadius: 10,  // spawn scatter radius
    clusterSize: 10,           // particles per burst
    delay: Duration(milliseconds: 300),
    recycles: false,           // true = loop forever
  ),
  ...
)

Burst Emitter #

Fire a fixed number of particles in a single burst — configurable spread, repeat interval, physics, and optional manual controller.

Import: import 'package:particles_flutter/engine.dart';

Spread patterns

Pattern Description
RadialBurst All directions evenly
ConeBurst Within a configurable cone angle
DirectionalBurst One direction with spread
CustomBurst Offset Function(int index, int total) — build your own

One-shot radial explosion

Particles(
  particles: const [],
  width: size.width,
  height: size.height,
  boundType: BoundType.None,
  burstEmitters: [
    BurstEmitter(
      position: (size) => size.center(Offset.zero),
      particleCount: 60,
      pattern: RadialBurst(minSpeed: 150, maxSpeed: 400),
      repeatCount: 1,
      physics: ParticlePhysics(gravityScale: 120),
      particleFactory: (i, total) => CircularParticle(
        radius: 4,
        color: Colors.orange,
        velocity: Offset.zero,
        lifetime: 1.8,
        endOpacity: 0.0,
      ),
    ),
  ],
)

Cone confetti from bottom

BurstEmitter(
  position: (size) => Offset(size.width / 2, size.height),
  particleCount: 50,
  pattern: ConeBurst(
    angle: -pi / 2,    // shoot upward
    spread: pi / 2.5,
    minSpeed: 300,
    maxSpeed: 600,
  ),
  repeatCount: 1,
  positionRadius: 60,
  physics: ParticlePhysics(gravityScale: 200),
  particleFactory: (i, total) => RoundRectangularParticle(
    width: 12, height: 4, cornerRadius: 2,
    color: Colors.pink,
    velocity: Offset.zero,
    rotationSpeed: 3.0,
    lifetime: 2.0,
    endOpacity: 0.0,
  ),
)

Tap-to-burst at touch point

// In your State:
final _ctrl = BurstEmitterController();
Offset _tapPos = Offset.zero;

// In build:
Listener(
  behavior: HitTestBehavior.opaque,
  onPointerDown: (event) {
    _tapPos = event.localPosition;
    _ctrl.trigger();
  },
  child: Particles(
    particles: const [],
    width: size.width,
    height: size.height,
    boundType: BoundType.None,
    burstEmitters: [
      BurstEmitter(
        position: (size) => _tapPos,
        particleCount: 40,
        pattern: RadialBurst(minSpeed: 80, maxSpeed: 240),
        repeatCount: 0,       // 0 = fire only when triggered
        controller: _ctrl,
        physics: ParticlePhysics(gravityScale: 80),
        particleFactory: (i, total) => CircularParticle(
          radius: 3,
          color: Colors.cyan,
          velocity: Offset.zero,
          lifetime: 1.2,
          endScale: 0.0,
          endOpacity: 0.0,
        ),
      ),
    ],
  ),
)

BurstEmitter parameters

Parameter Type Default Description
position Offset Function(Size) required Burst origin
particleCount int required Particles per burst
particleFactory Particle Function(int, int) required Builds each particle
pattern BurstPattern required Velocity spread strategy
initialDelay Duration Duration.zero Delay before first burst
repeatCount int 1 Bursts to fire; 0 = controller-only
repeatInterval Duration Duration.zero Gap between repeats
positionRadius double 0 Scatter radius around position
physics ParticlePhysics? null Gravity for burst particles
enableTrails bool false Trail rendering (expensive at high counts)
controller BurstEmitterController? null Manual trigger
maxPoolSize int 500 Memory ceiling; oldest particles reclaimed when full

Emitter.burst(...) is a shorthand factory that returns a BurstEmitter — use whichever style you prefer.


Example Scenes #

✨ Starfield #

Particles(
  boundType: BoundType.WrapAround,
  interaction: ParticleInteraction(awayRadius: 120, enableHover: true),
  particles: List.generate(120, (_) => CircularParticle(
    radius: Random().nextDouble() * 3 + 0.5,
    color: Colors.white.withValues(alpha: 0.7),
    velocity: Offset((Random().nextDouble() - 0.5) * 40,
                     (Random().nextDouble() - 0.5) * 40),
  )),
  ...
)

❄️ Snow #

Particles(
  boundType: BoundType.WrapAround,
  particlePhysics: ParticlePhysics(gravityScale: 20),
  particles: List.generate(100, (_) => CircularParticle(
    radius: Random().nextDouble() * 6 + 2,
    color: Colors.white.withValues(alpha: 0.8),
    velocity: Offset((Random().nextDouble() - 0.5) * 20,
                     Random().nextDouble() * 15 + 5),
  )),
  ...
)

🎆 Fireworks #

Particles(
  boundType: BoundType.None,
  particlePhysics: ParticlePhysics(gravityScale: 45),
  particleEmitter: Emitter(
    startPosition: Offset(width / 2, height / 2),
    startPositionRadius: width * 0.25,
    clusterSize: 15,
    delay: Duration(milliseconds: 300),
  ),
  particles: List.generate(150, (_) {
    final angle = Random().nextDouble() * 2 * pi;
    final speed = Random().nextDouble() * 120 + 60;
    return TriangularParticle(
      width: 6, height: 6,
      color: Colors.orange,
      velocity: Offset(cos(angle) * speed, sin(angle) * speed),
      rotationSpeed: 3.0,
    );
  }),
  ...
)

☄️ Comets — color gradient + trails #

Particles(
  boundType: BoundType.WrapAround,
  particles: List.generate(80, (_) {
    final angle = Random().nextDouble() * 2 * pi;
    final speed = Random().nextDouble() * 60 + 40;
    return CircularParticle(
      radius: Random().nextDouble() * 3 + 2,
      color: Colors.white,
      velocity: Offset(cos(angle) * speed, sin(angle) * speed),
      lifetime: Random().nextDouble() * 2.0 + 1.5,
      colorGradient: [Colors.white, Colors.yellow, Colors.orange, Colors.transparent],
      startOpacity: 0.0,
      endOpacity: 0.0,
      trailEnabled: true,
      trailLength: 7,
      trailFade: true,
    );
  }),
  ...
)

🔮 Pulse — scale over lifetime #

Particles(
  boundType: BoundType.WrapAround,
  particles: List.generate(60, (_) => CircularParticle(
    radius: 10,
    color: Color(0xFF7C4DFF),
    velocity: Offset((Random().nextDouble() - 0.5) * 20,
                     (Random().nextDouble() - 0.5) * 20),
    lifetime: 2.5,
    startScale: 0.1,
    endScale: 1.8,
    scaleCurve: Curves.easeInOut,
    startOpacity: 0.0,
    endOpacity: 0.0,
  )),
  ...
)

👻 Ghosts — fade in/out #

Particles(
  boundType: BoundType.WrapAround,
  particles: List.generate(50, (_) => CircularParticle(
    radius: Random().nextDouble() * 14 + 8,
    color: Color(0xFF69F0AE),
    velocity: Offset((Random().nextDouble() - 0.5) * 15,
                     (Random().nextDouble() - 0.5) * 10),
    lifetime: Random().nextDouble() * 3.0 + 2.0,
    startOpacity: 0.0,
    endOpacity: 0.0,
    startScale: 0.6,
    endScale: 1.2,
    scaleCurve: Curves.easeOut,
  )),
  ...
)

🚀 Rockets — all lifetime features combined #

Particles(
  boundType: BoundType.None,
  particlePhysics: ParticlePhysics(gravityScale: 40),
  particles: List.generate(150, (_) {
    final angle = Random().nextDouble() * 2 * pi;
    final speed = Random().nextDouble() * 80 + 80;
    return CircularParticle(
      radius: Random().nextDouble() * 4 + 2,
      color: Colors.yellow,
      velocity: Offset(cos(angle) * speed, sin(angle) * speed),
      lifetime: Random().nextDouble() * 1.0 + 1.2,
      colorGradient: [Colors.white, Colors.yellow, Colors.red, Colors.transparent],
      startScale: 1.0,
      endScale: 0.0,
      scaleCurve: Curves.easeIn,
      startOpacity: 0.0,
      endOpacity: 0.0,
      trailEnabled: true,
      trailLength: 6,
      trailFade: true,
    );
  }),
  ...
)

💥 Burst — tap-to-explode #

final ctrl = BurstEmitterController();
Offset tapPos = Offset.zero;

Listener(
  behavior: HitTestBehavior.opaque,
  onPointerDown: (e) { tapPos = e.localPosition; ctrl.trigger(); },
  child: Particles(
    particles: const [],
    width: size.width,
    height: size.height,
    boundType: BoundType.None,
    burstEmitters: [
      BurstEmitter(
        position: (size) => tapPos,
        particleCount: 40,
        pattern: RadialBurst(minSpeed: 100, maxSpeed: 300),
        repeatCount: 0,
        controller: ctrl,
        physics: ParticlePhysics(gravityScale: 80),
        particleFactory: (i, _) => CircularParticle(
          radius: Random().nextDouble() * 3 + 1.5,
          color: Colors.orange,
          velocity: Offset.zero,
          lifetime: 1.2,
          endScale: 0.0,
          endOpacity: 0.0,
        ),
      ),
    ],
  ),
)

Live Demo #

See all scenes running live → particles-flutter.vercel.app

 


Changelog #

v3.0 #

  • Breaking: Dart SDK >=3.0.0 required (Flutter 3.10+). Projects on Dart 2.x must upgrade first.
  • BurstEmitter — fire fixed particle counts in radial, cone, directional, or custom spread patterns
  • BurstEmitterController — trigger bursts manually from gestures, game events, or any code
  • Tap-to-burstBurstEmitterController.trigger() + onPointerDown for per-tap explosions
  • Overlap-safe pooling — multiple bursts in flight simultaneously; memory hard-capped at maxPoolSize

v2.1 #

  • Color over lifetime — smooth two-color or gradient transitions
  • Scale over lifetime — grow/shrink with curve support
  • Fade over lifetime — fade in, out, or triangle (both ends zero = auto mid-peak)
  • Particle trails — motion trails with configurable length and fade
  • Object pooling for ParticleLine — reduced GC pressure on line-connected scenes
  • Performance improvements — touch interaction, physics, and emitter update loops

All releases are fully backward compatible — no changes needed to existing code.


Support #

If this package saved you time:

Buy Me A Coffee

Contributing #

Bug reports and pull requests welcome.

Contributors #

RealEeveahy
191
likes
150
points
1.87k
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A flutter package for particle animation. You can control the animation speed, number of Particles on the screen, dimension and shape of particle via connecting lines,etc.

Repository (GitHub)
View/report issues

License

BSD-2-Clause (license)

Dependencies

flutter

More

Packages that depend on particles_flutter