Tutorial · 12 min · intermediate

Lottie in the hierarchy

After Effects animations on the native lottie-ios / lottie-android engines — a CDN sticker grid of 78 live animations that stays smooth and flat on memory.

What you'll build — running on device.

Lottie is how motion designers ship: export from After Effects, play the JSON. dartnative_lottie renders those files with the platforms’ own engines — lottie-ios and lottie-android, the same libraries behind most native apps’ animations. You’ll build the hardest version of the canonical use: a sticker picker where every cell is a live, running animation, streamed from a CDN through a recycled grid — 78 of them, scrolling smoothly, with flat memory. It’s the DartNative playground’s Lottie grid demo, carried into the tutorial byte-for-byte.

What you need

That’s the whole setup — no assets to bundle. Every animation on this screen arrives over the network.

Step 1 — Stickers from a CDN

Lottie.network streams .json files or .zip bundles (the dotLottie-style zips most sticker CDNs serve), caches them on disk, and shows a placeholder widget while fetching. The screen’s data is just a list of URLs:

/// The 26 unique sticker-pack URLs (…/0.zip … 25.zip).
final stickerUrlsBase = List.generate(26,
    (i) => 'https://cdn.presence.is/stickers/cl69ghdwt000100bx966hxbp6/$i.zip');

/// Grid data: the unique set repeated 3× (scroll / recycling test data).
final _stickerUrls = [for (var r = 0; r < 3; r++) ...stickerUrlsBase];

And each cell is one widget:

Lottie.network(
  _stickerUrls[i],
  loop: true,
  autoplay: true,
  fit: LottieFit.contain,
  placeholder: _shimmer,
),

autoplay: true means the sticker starts moving the moment its cell appears; loop: true keeps it moving. The animation is a real native view in the hierarchy — no Dart-side rasterizing.

Step 2 — The loading state users actually read

The placeholder is a shimmer — the animated grey pulse users read as “loading” — built once and reused by every cell:

static final _shimmer = Shimmer.fromColors(
  baseColor: kTileBg,
  highlightColor: kChipBg,
  child: Container(color: kTileBg),
);

(kTileBg and kChipBg are theme getters from the playground’s shared UI kit, lib/screens/home/demo_ui.dart, which the screen imports.)

The 26 unique packs are repeated three times so the grid is long enough to actually scroll — which is the point of the next step.

Step 3 — 78 live animations, flat memory

Every cell in this grid is a running animation, and a running animation holds keyframe buffers and a live render layer. That memory isn’t image decode, so the cacheWidth trick from the photo grid can’t touch it. What does is content windowing — telling FastGrid to keep only a window of items alive around the viewport:

FastGrid(
  itemCount: _stickerUrls.length,
  crossAxisCount: 3,
  childAspectRatio: 1.0,
  mainAxisSpacing: 4,
  crossAxisSpacing: 4,
  padding: const EdgeInsets.all(4),
  keepAliveCount: 15,
  itemBuilder: (_, i) => Lottie.network(...),
),

keepAliveCount counts items, not rows: 15 items at 3 columns is about 5 rows kept alive on each side of the screen. Stickers that scroll further away than that are disposed — buffers, render layer, everything — and rebuilt from the disk-cached .zip as they near the viewport again. The result is a sticker picker whose memory stays flat no matter how long the grid gets, while the visible cells never stop animating.

Going further — driving playback from Dart

The Lottie widget also takes a controller: — a LottieController with the full transport (play, pause, stop, setSpeed, setLoopMode, and setProgress for timeline scrubbing). The playground’s Lottie controls demo shows all of it; for a tap-triggered burst (a like button), set autoplay: false and call _controller.play() from a gesture.

Why this is native

The frames are rendered by lottie-ios and lottie-android — the engines Airbnb built and the native ecosystem battle-tested — drawing into real platform layers, not re-rasterized through a Dart canvas. Your motion designer’s exports play with the exact fidelity and performance native apps get, and the animations composite with everything else in the (single) native hierarchy.

The finished code

In the public repo — dn create ., dn run. lib/screens/lottie/lottie_grid_demo.dart is a byte-identical copy of the playground’s Lottie grid demo (lib/screens/home/demo_ui.dart is the playground’s shared UI kit, also verbatim); only the thin lib/main.dart entry is tutorial-specific. When the playground screen improves, this tutorial inherits it by copying the file again.

Open the finished code →