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
- A project from Your first DartNative app
dartnative_lottie: ^1.0.0in your pubspec
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-iosandlottie-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.