flutter_svga_easyplayer 0.0.7 copy "flutter_svga_easyplayer: ^0.0.7" to clipboard
flutter_svga_easyplayer: ^0.0.7 copied to clipboard

A Flutter package for rendering SVGA animations with dynamic customization, smooth playback, cache control, and powerful EasyPlayer features.

flutter_svga_easyplayer

A production-ready SVGA renderer for Flutter with an easy-to-use player, intelligent caching, silent background precaching, dynamic content overrides, and integrated audio playback.

pub version pub points pub likes license


Table of contents #


Highlights #

  • SVGAEasyPlayer — drop-in widget; play from assets or network with one line of code.
  • Three playback modes — infinite loop, play once, or repeat N times, each with an onFinished callback.
  • Audio control — mute/unmute embedded audio tracks at the widget or controller level.
  • Intelligent disk cache — persistent, size- and age-bounded, shared across widgets and app launches.
  • Silent background precaching — hand it a list of URLs and it warms the cache on its own; later playback starts instantly from disk.
  • Poison-resistant cache — payloads are validated before they reach the cache and corrupted entries are self-healed on read.
  • Dynamic content — override text, swap images, add custom drawings, or hide layers at runtime.
  • First-class controller — full AnimationController API with frame seeking, status listeners, and SingleTickerProviderStateMixin support.

Install #

Add the package to your pubspec.yaml:

dependencies:
  flutter_svga_easyplayer: ^0.0.7

Then run:

flutter pub get

Import it where needed:

import 'package:flutter_svga_easyplayer/flutter_svga_easyplayer.dart';

Quick start #

The minimum you need to render an SVGA file:

import 'package:flutter/material.dart';
import 'package:flutter_svga_easyplayer/flutter_svga_easyplayer.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('SVGA Demo')),
        body: const Center(
          child: SVGAEasyPlayer(
            assetsName: 'assets/sample.svga',
            fit: BoxFit.contain,
          ),
        ),
      ),
    );
  }
}

Don't forget to register the asset in pubspec.yaml:

flutter:
  assets:
    - assets/sample.svga

Loading animations #

SVGAEasyPlayer accepts either an asset path or a network URL.

From assets #

SVGAEasyPlayer(
  assetsName: 'assets/sample.svga',
  fit: BoxFit.contain,
);

From a network URL #

SVGAEasyPlayer(
  resUrl: 'https://example.com/sample.svga',
  fit: BoxFit.cover,
);

Network loads are cached automatically on first successful decode; see Caching and Background precaching for how to make subsequent renders instantaneous.


Playback modes #

SVGAEasyPlayer exposes three playback modes through the loops parameter.

loops Behaviour onFinished
null Infinite loop (default) never fires
0 Play once after 1 play
n > 0 Play once, then repeat n times after n + 1
// Infinite loop — perfect for loaders and backgrounds.
SVGAEasyPlayer(assetsName: 'assets/loading.svga');

// Play once — perfect for splash / intro animations.
SVGAEasyPlayer(
  assetsName: 'assets/splash.svga',
  loops: 0,
  onFinished: () => Navigator.of(context).pushReplacementNamed('/home'),
);

// Repeat three times — perfect for celebration effects.
SVGAEasyPlayer(
  assetsName: 'assets/celebration.svga',
  loops: 3,
  onFinished: () => debugPrint('done!'),
);

See PLAYBACK_MODES.md for a deeper guide and more recipes.


Audio control #

SVGA files can embed audio tracks. Mute or unmute them without stopping the animation:

// From the widget.
SVGAEasyPlayer(
  assetsName: 'assets/with_audio.svga',
  isMute: true,
);

// From a controller you own.
final controller = SVGAAnimationController(vsync: this);
controller.isMute = true;

Toggling isMute at runtime takes effect on the next frame — no reload required.


Caching #

A persistent disk cache is enabled by default. Entries are keyed by URL (or assets:<path> for bundled assets), bounded by size and age, and survive app restarts.

Per-widget controls #

// Default: read from cache if available, store on first successful load.
SVGAEasyPlayer(resUrl: 'https://cdn.example.com/anim.svga');

// Always fetch fresh — useful for dynamic content.
SVGAEasyPlayer(
  resUrl: 'https://api.example.com/dynamic.svga',
  useCache: false,
);

// One-shot animation — clear the entry when the widget disposes.
SVGAEasyPlayer(
  assetsName: 'assets/one_time.svga',
  loops: 0,
  clearCacheOnDispose: true,
  onFinished: () => Navigator.pop(context),
);

Global configuration #

// Tune once at startup (all values optional).
SVGACache.shared
  ..setMaxCacheSize(100 * 1024 * 1024)        // 100 MB
  ..setMaxAge(const Duration(days: 7));        // evict after 7 days

// Inspect or manage the cache.
final exists  = await SVGACache.shared.contains('https://…/anim.svga');
final stats   = await SVGACache.shared.getStats();
await SVGACache.shared.remove('https://…/anim.svga');
await SVGACache.shared.clear();

Reliability: corrupt payloads (e.g. an HTML 404 page returned with a 200 status by a misconfigured CDN) are detected and never written to the cache. If a previously cached entry ever fails to decode it is evicted and refetched automatically — playback self-heals without any code on your side.

For advanced cache patterns see CACHE.md and CACHE_CONTROL.md.


Background precaching #

Hand SVGAPrecacheManager a list of URLs at startup (or any time), and it silently downloads and stores them in the same cache the player reads from. When you later render SVGAEasyPlayer(resUrl: url) for any of those URLs, it plays instantly — no network round-trip, no loading indicator flash.

In main() #

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Fire-and-forget — returns immediately, work happens in the background.
  SVGAPrecacheManager.shared.precache(
    const [
      'https://cdn.example.com/a.svga',
      'https://cdn.example.com/b.svga',
      'https://cdn.example.com/c.svga',
    ],
    delay:       const Duration(seconds: 1),  // let startup settle first
    concurrency: 3,                            // parallel downloads
    timeout:     const Duration(seconds: 15),  // per-request timeout
  );

  runApp(const MyApp());
}

From any screen #

You are not limited to main(). Precache from initState, after login, on a button tap, or whenever your app knows which animations are coming:

@override
void initState() {
  super.initState();
  SVGAPrecacheManager.shared.precache(const [
    'https://cdn.example.com/home_hero.svga',
    'https://cdn.example.com/home_badge.svga',
  ]);
}

With progress or a result #

final result = await SVGAPrecacheManager.shared.precache(
  urls,
  onProgress: (done, total, url, ok) {
    debugPrint('[$done/$total] ${ok ? "OK" : "FAIL"}  $url');
  },
);

// SVGAPrecacheResult(total: 6, completed: 6, hits: 2, fetched: 4, failed: 0, cancelled: false)
debugPrint('$result');

Assets, cancellation, and state #

// Warm bundled assets.
SVGAPrecacheManager.shared.precacheAssets(const [
  'assets/intro.svga',
  'assets/celebration.svga',
]);

// Cancel any in-flight batch; in-progress downloads still finish.
SVGAPrecacheManager.shared.cancel();

// Check whether anything is running.
if (SVGAPrecacheManager.shared.isRunning) { /* … */ }

Why it's safe to call on every launch #

  • Skip-if-cached — URLs with a valid entry are skipped without any network I/O.
  • Deduplicated — overlapping batches requesting the same URL result in a single download.
  • Failure-tolerant — network, timeout, asset, and disk errors are swallowed; one bad URL never blocks the rest.
  • Validated before storage — only payloads that look like a valid SVGA (zlib) file are written to the cache.
  • Global-setting aware — honours SVGACache.shared's enable flag, maxCacheSize, and maxAge.

Dynamic content #

Replace text, swap images, add a custom drawer, or hide a layer at runtime. These operate on the dynamicItem of the decoded MovieEntity.

Replace text #

controller.videoItem!.dynamicItem.setText(
  TextPainter(
    text: const TextSpan(
      text: 'Hello SVGA!',
      style: TextStyle(color: Colors.red, fontSize: 18),
    ),
    textDirection: TextDirection.ltr,
  ),
  'text_layer',
);

Swap an image #

controller.videoItem!.dynamicItem.setImageWithUrl(
  'https://example.com/new_image.png',
  'image_layer',
);

Draw on top of a layer #

controller.videoItem!.dynamicItem.setDynamicDrawer(
  (canvas, frameIndex) {
    canvas.drawRect(
      const Rect.fromLTWH(0, 0, 88, 88),
      Paint()..color = Colors.red,
    );
  },
  'banner',
);

Hide a layer #

controller.videoItem!.dynamicItem.setHidden(true, 'layer_to_hide');

Using the controller directly #

When you need manual control — for timeline scrubbing, status listeners, or interop with other animations — use SVGAAnimationController and SVGAImage directly:

class MyAnimated extends StatefulWidget {
  const MyAnimated({super.key});

  @override
  State<MyAnimated> createState() => _MyAnimatedState();
}

class _MyAnimatedState extends State<MyAnimated>
    with SingleTickerProviderStateMixin {
  late final SVGAAnimationController _controller =
      SVGAAnimationController(vsync: this)..isMute = true;

  @override
  void initState() {
    super.initState();
    SVGAParser.shared.decodeFromAssets('assets/sample.svga').then((video) {
      if (!mounted) {
        video.dispose();
        return;
      }
      _controller
        ..videoItem = video
        ..repeat();
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => SVGAImage(_controller);
}

Controller playback controls:

controller.forward();  // play once
controller.repeat();   // loop
controller.stop();     // pause
controller.value = 0;  // seek to first frame

API reference #

SVGAEasyPlayer #

Parameter Type Default Description
assetsName String? Path to a bundled asset. Mutually exclusive with resUrl.
resUrl String? Network URL. Mutually exclusive with assetsName.
fit BoxFit contain How the animation is scaled inside the widget.
loops int? null null = infinite, 0 = play once, n > 0 = play once + repeat n.
onFinished VoidCallback? Called after the configured playback completes.
isMute bool false Mute embedded audio.
useCache bool true Read from and write to the global cache for this widget.
clearCacheOnDispose bool false Remove the cache entry for this source when the widget is disposed.

SVGAParser #

Call Description
SVGAParser.shared.decodeFromURL(url) Decode from a URL (uses and populates the cache).
SVGAParser.shared.decodeFromAssets(path) Decode from a bundled asset (uses and populates cache).
SVGAParser.shared.decodeFromBuffer(bytes) Decode a raw List<int>/Uint8List payload.

SVGACache #

Call Description
setEnabled(bool) Globally enable or disable caching.
setMaxCacheSize(bytes) Set the cache size ceiling (default 100 MB).
setMaxAge(Duration) Set the per-entry expiry (default 7 days).
contains(source) Whether a valid entry exists for source.
remove(source) Delete one entry.
clear() Delete every entry.
getStats() {enabled, size, maxSize, fileCount, maxAge} snapshot.

SVGAPrecacheManager #

Call Description
precache(urls, {delay, concurrency, timeout, onProgress}) Fire-and-forget precache of network URLs. Returns a result.
precacheAssets(paths, {delay, concurrency, onProgress}) Same, for bundled assets.
cancel() Stop picking up new items from in-flight batches.
isRunning true while any batch is still active.

SVGAAnimationController #

Inherits from AnimationController — use forward, repeat, stop, reset, value, and status listeners as usual. Adds:

Member Description
videoItem The decoded MovieEntity to render.
isMute Mute embedded audio.
currentFrame Current frame index (0-based).
frames Total frame count.

Platform support #

Platform Rendering Audio
Android
iOS
macOS
Linux
Windows
Web

Troubleshooting #

A black box where the animation should be. The asset is missing or not registered. Add it under flutter/assets: in pubspec.yaml and re-run flutter pub get.

Exception caught by SVGAEasyPlayer: Filter error, bad data. The bytes fed to the decoder are not a valid SVGA payload. Since 0.0.7 the cache refuses to store bad payloads and self-heals previously poisoned entries on next read, so a single hiccup cannot keep failing forever. Check that the URL actually returns an .svga file (try curl -I <url> or open it in a browser).

Network URL won't load. Serve the file over HTTPS, verify it in a browser, and make sure platform network permissions are configured (e.g. iOS ATS exceptions for plain HTTP during development).

Animation renders but never moves. You probably set the videoItem without calling forward(), repeat(), or any playback method on the controller.


Further reading #


Contributing #

Maintainer #

Contributors #

Contributor Contribution
wonderkidshihab Fixed repeated music playback bug (#3)

License #

Released under the MIT License.

2
likes
160
points
155
downloads

Documentation

API reference

Publisher

verified publisherzamansheikh.com

Weekly Downloads

A Flutter package for rendering SVGA animations with dynamic customization, smooth playback, cache control, and powerful EasyPlayer features.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

archive, audioplayers, crypto, flutter, http, path_drawing, path_provider, protobuf

More

Packages that depend on flutter_svga_easyplayer