flutter_vap_kit

Transparent video animations for Flutter — gift effects, entry animations,
banners and badges — in one line.

pub version pub points platforms BSD-3-Clause license

An animated banner with a Go button on top, and a looping rocket

VapPlayer.asset('assets/rocket.mp4', loop: true)

That is the whole integration. No controller to create, no waiting for a native view, no restarting the clip yourself to make it loop.


What is VAP?

VAP is Tencent's format for video with a real alpha channel: an ordinary H.264 MP4 that carries its own transparency. It is what live-streaming and social apps use for full-screen gift effects, because it is far smaller than a PNG sequence, far richer than Lottie or SVGA can be, and hardware-decoded.

flutter_vap_kit plays those files on Android and iOS.

Why this package

Playing a VAP file is easy. Playing it well inside a Flutter app is where the time goes. This package handles the parts you would otherwise write yourself:

You get What it means
Seamless looping Loops run inside the native player on both platforms. No blink between runs.
Never an empty box The clip's own first frame is shown, with its transparency, until playback starts.
No dropped starts Play requests wait for the native view and are retried if it does not answer.
Assets copied once Bundled clips are extracted one time, not on every play.
Network clips cached Downloaded once, then played from disk.
Touches pass through Buttons under or over an animation keep working.
Survives backgrounding Looping clips resume when the app comes back, and restart themselves if the decoder is lost.
Works on the iOS simulator Most VAP plugins show nothing there; this one renders.
Declarative Change source, loop or fit and the player follows.

Install

dependencies:
  flutter_vap_kit: ^0.1.2
Minimum
Android minSdk 24
iOS 12.0 — Swift Package Manager or CocoaPods

No permissions, no manifest or Info.plist changes.

Quick start

A looping animation

SizedBox(
  height: 260,
  child: VapPlayer.asset('assets/rocket.mp4', loop: true),
)

The player fills the box its parent gives it, so give it a size: a SizedBox, an AspectRatio, a Positioned.

A one-shot effect

VapPlayer.network(
  'https://example.com/gift.mp4',
  onComplete: () => showNextGift(),
)

An animated banner with a button on top

Stack(
  alignment: Alignment.centerRight,
  children: [
    AspectRatio(
      aspectRatio: 750 / 176,
      child: VapPlayer.asset('assets/banner.mp4', loop: true, fit: BoxFit.fill),
    ),
    FilledButton(onPressed: join, child: const Text('Go')),
  ],
)

A gift queue, driven from code

final controller = VapController();

VapPlayer.asset(
  'assets/gift.mp4',
  controller: controller,
  autoPlay: false,
  onComplete: playNextGift,
);

controller.play();                                        // the player's clip
controller.play(const VapSource.network(nextGiftUrl));    // a different clip
controller.stop();

controller.state is idle, loading, playing, completed or error. The controller is a Listenable, so it works with ListenableBuilder.

Keep a widget in step with the clip

Some clips have a moving slot for a profile picture or a name. onFrame tells you which video frame is on screen, so your widget can follow it:

VapPlayer.asset(
  'assets/winner.mp4',
  onFrame: (frame) => position.value = slotPositions[frame],
)

The frame reaches the screen slightly after it is reported (a frame or two, depending on the device), so trail the reported index a little if the match has to be exact.

Warm clips up before they are needed

await VapKit.preload(const VapSource.network('https://example.com/gift.mp4'));

Copies or downloads the clip and renders its first frame, so the player that shows it later starts at once. Good for the gifts on a gift panel.

API at a glance

VapPlayer

Parameter Default
source — VapSource.asset, .file or .network. Or use the VapPlayer.asset / .file / .network constructors.
loop false Repeat forever, seamlessly.
repeat 1 Times to play when loop is false.
autoPlay true Start as soon as the player is on screen.
fit BoxFit.contain contain, cover or fill.
showPoster true Show the first frame while the clip gets ready.
placeholder — Your own widget for that moment instead.
controller — A VapController, when you need one.
onStart / onComplete / onError — Playback callbacks.
onFrame — Index of each video frame as it is drawn, to keep your own widgets in step with the clip.

VapKit

VapKit.preload(source) Get a clip ready ahead of time.
VapKit.clearCache() Forget cached clips and delete downloads.

Making VAP files

Export your animation as a PNG sequence with transparency, then convert it with Tencent's VapTool. The result is a single .mp4 you drop into assets/.

Good to know

  • One player per animation. Each VapPlayer owns a hardware video decoder. A handful on screen is fine; a long scrolling list of them is not — show a still there and play on tap.
  • Fusion animations (text or images merged into a clip at play time) are not exposed yet.
  • Do not combine with other VAP plugins. This package ships its own copy of the iOS player, so adding another plugin that brings QGVAPlayer produces duplicate classes.

Example

The example app is the scene in the preview above: an animated banner with text and a working button laid over it, a looping rocket, and a one-shot effect fired from a button.

cd example
flutter run

Credits and license

BSD-3-Clause — see LICENSE.

Built on Tencent VAP, which is MIT-licensed and stays so. The Android side uses Tencent's published library; the iOS side includes a copy of Tencent's player source with two fixes — a crash in decoder set-up, and rendering when frames are software-decoded — see ios/flutter_vap_kit/Sources/flutter_vap_kit_player/LICENSE.txt.

Libraries

flutter_vap_kit
Play Tencent VAP (alpha-channel MP4) animations with a single widget.