Snapshot do README original do repositório-fonte vinculado. Consulte o repositório para o conteúdo mais recente.
flutter_spritesheet_animation
Lightweight spritesheet animation widget for Flutter. Supports grid-based and JSON atlas (TexturePacker, Aseprite) spritesheets with named animations, playback modes, and an external controller.
Zero external dependencies - uses only the Flutter SDK.
Features
- Grid-based spritesheet animation (uniform frame grid)
- JSON atlas animation (TexturePacker hash/array, Aseprite)
- Named animations via
frameTags
- Playback modes: forward, reverse, pingPong
- External controller for programmatic control
- Per-frame duration support from atlas data
- Pre-computed frame rects for optimal rendering
- Configurable FPS, looping, blend mode, box fit
- Speed multiplier that also works on atlas animations with per-frame durations
- Fixed repeat counts with per-cycle callbacks
Installation
dependencies:
flutter_spritesheet_animation: ^1.0.2
Usage
Grid Mode
For spritesheets with frames arranged in a uniform grid:
SpriteAnimation.grid(
image: AssetImage('assets/explosion.png'),
columns: 8,
rows: 4,
frameCount: 30,
fps: 24,
width: 128,
height: 128,
)
Atlas Mode
For spritesheets defined by a JSON atlas (TexturePacker or Aseprite export):
final atlas = await SpriteAtlas.fromAsset('assets/character.json');
SpriteAnimation.atlas(
image: AssetImage('assets/character.png'),
atlas: atlas,
animation: 'idle',
width: 128,
height: 128,
)
Controller
Use SpriteAnimationController for programmatic playback control:
final controller = SpriteAnimationController(
fps: 24,
mode: PlayMode.pingPong,
loop: true,
autoPlay: false,
);
SpriteAnimation.grid(
image: AssetImage('assets/explosion.png'),
columns: 8,
rows: 4,
controller: controller,
)
controller.play();
controller.pause();
controller.stop();
controller.goToFrame(5);
controller.fps = 30;
controller.mode = PlayMode.reverse;
controller.loop = false;
controller.setAnimation('walk', atlas: atlas);
controller.isPlaying;
controller.currentFrame;
controller.totalFrames;
controller.animationName;
controller.dispose();
Precaching
Pre-cache spritesheet images to eliminate loading flash:
await SpriteAnimation.precache(
const AssetImage('assets/explosion.png'),
context,
);
await SpriteAnimation.precacheAll([
const AssetImage('assets/idle.png'),
const AssetImage('assets/walk.png'),
const AssetImage('assets/attack.png'),
], context);
Callbacks
SpriteAnimation.grid(
image: AssetImage('assets/explosion.png'),
columns: 8,
rows: 4,
loop: false,
onFrame: (frame) => print('Frame: $frame'),
onComplete: () => print('Animation finished'),
)
Atlas JSON Formats
TexturePacker (hash):
{
"frames": {
"idle_0.png": {
"frame": { "x": 0, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 }
}
},
"meta": {
"size": { "w": 256, "h": 256 },
"frameTags": [
{ "name": "idle", "from": 0, "to": 3 },
{ "name": "walk", "from": 4, "to": 9 }
]
}
}
Aseprite (array):
{
"frames": [
{
"filename": "idle_0.png",
"frame": { "x": 0, "y": 0, "w": 64, "h": 64 },
"duration": 100
}
],
"meta": {
"frameTags": [
{ "name": "idle", "from": 0, "to": 3 }
]
}
}
Both formats are auto-detected.
Performance
Optimized for smooth 60fps animation rendering:
- Zero widget rebuilds: Uses
CustomPainter(repaint: controller) — frame changes repaint only the paint layer, skipping build() entirely
- Zero-allocation tick loop: Raw microsecond arithmetic instead of
Duration objects in the hot path
- Pre-computed frame rects: Grid source rectangles are calculated once at load time, not during paint
- Reusable Paint object: Single Paint instance reused across frames, avoiding per-frame allocations
- Accumulator-based timing: Frame advancement uses a time accumulator for accurate frame pacing
- Image precaching:
precache() / precacheAll() to eliminate loading flash
- Multiple instances: Each widget runs independently with its own controller, ticker, and paint layer
- No external dependencies: Zero overhead from third-party packages
API Reference
SpriteAnimation
| Property |
Type |
Default |
Description |
image |
ImageProvider |
required |
Spritesheet image |
columns |
int |
- |
Grid columns (grid mode) |
rows |
int |
- |
Grid rows (grid mode) |
frameCount |
int? |
columns*rows |
Total frames (grid mode) |
atlas |
SpriteAtlas |
- |
Atlas data (atlas mode) |
animation |
String? |
first |
Animation name (atlas mode) |
fps |
double |
12 |
Frames per second |
autoPlay |
bool |
true |
Start automatically |
loop |
bool |
true |
Loop animation |
mode |
PlayMode |
forward |
Playback direction |
blendMode |
BlendMode |
srcOver |
Canvas blend mode |
fit |
BoxFit |
contain |
How sprite fits in bounds |
width |
double? |
- |
Fixed width |
height |
double? |
- |
Fixed height |
controller |
SpriteAnimationController? |
- |
External controller |
onFrame |
ValueChanged<int>? |
- |
Frame change callback |
onComplete |
VoidCallback? |
- |
Completion callback |
SpriteAnimationController
| Method |
Description |
play() |
Start or resume playback |
pause() |
Pause at current frame |
stop() |
Stop and reset to frame 0 |
goToFrame(int) |
Jump to specific frame |
setAnimation(String, {SpriteAtlas}) |
Switch named animation |
dispose() |
Clean up resources |
| Property |
Type |
Default |
Description |
speed |
double |
1 |
Playback multiplier applied to the whole clock |
repeatCount |
int? |
null |
Cycles to play before completing; null is unlimited |
completedCycles |
int |
0 |
Cycles finished since playback started |
onCycle |
ValueChanged<int>? |
- |
Fires on every wrap of a looping animation |
Speed
fps sets the base frame rate, but atlas animations carry per-frame
durations and ignore it. speed scales the clock either way:
final controller = SpriteAnimationController(speed: 2);
controller.speed = 0.5;
Fixed repeats
final controller = SpriteAnimationController(repeatCount: 3)
..onCycle = (done) => debugPrint('cycle $done')
..onComplete = () => debugPrint('done');
repeatCount only applies while loop is true: a non-looping animation
always stops after one pass.
SpriteAtlas
| Method |
Description |
SpriteAtlas.fromJson(String) |
Parse from JSON string |
SpriteAtlas.fromAsset(String) |
Load from Flutter asset |
getAnimation(String) |
Get animation data by name |
animationNames |
List of available animation names |
License
MIT