Supported on: iOS SDK 1.0.61+ · Android SDK 1.0.33+ · Flutter SDK 1.0.3+
The AppDNA Flutter SDK renders rich media — Lottie animations, Rive animations, video, and images — through native platform pipelines on each side: SwiftUI + CoreAnimation on iOS, Jetpack Compose + Rive’s Android renderer on Android. Animations and effects look and feel identical to anything you would build by hand. All rich media content is configured in the AppDNA Console, no Dart code changes required.
Lottie Animations
Lottie animations are rendered natively on both platforms. On iOS the SDK uses lottie-ios (added as a separate SPM dependency in your iOS host project), and on Android Lottie Compose is bundled directly into the AppDNA SDK.
- Remote URL — provide a URL to a
.json Lottie file
- Inline JSON — embed the animation JSON directly in the config
- Controls — autoplay, loop, speed, play-on-scroll, play-on-tap
- Color overrides — remap colors in the animation without re-exporting
Lottie is supported in onboarding content blocks, paywall sections, in-app message hero areas, and survey thank-you screens.
Rive Animations
Rive animations are rendered using the native Rive runtime on each platform. iOS requires adding rive-ios as a separate SPM dependency in your iOS host project. The Android Rive runtime is bundled with the AppDNA Android SDK.
- Remote URL — provide a URL to a
.riv file
- Artboard selection — choose which artboard to display
- State machine — drive interactive animations with state machine inputs
- Trigger on completion — fire a state machine trigger when the step or flow completes
Rive is supported in onboarding content blocks and paywall sections.
Video
Inline video playback uses native players on each platform — AVPlayer on iOS, ExoPlayer on Android. Flutter sees a fully rendered native view, so playback performance is identical to a hand-written native implementation.
- Remote URL — provide a URL to an MP4 or HLS video
- Thumbnail — optional poster image shown before playback
- Controls — autoplay, loop, muted, show/hide controls
- Sizing — configurable height and corner radius
Video is supported in onboarding content blocks, paywall sections, and in-app message hero areas.
Images
Remote images are fetched and cached by the native SDKs:
- Remote URL — standard JPEG, PNG, and WebP supported on both platforms
- GIF — animated GIFs are supported in in-app message hero areas and push notification rich content
- SVG — supported on both platforms with native rasterization
- Corner radius, sizing, content mode — configured in the Console
Asset URL Conventions
Each rich-media block in the AppDNA Console uses a typed URL field:
| Field | Block type | Description |
|---|
lottie_url | lottie | Remote URL to a .json Lottie animation |
rive_url | rive | Remote URL to a .riv Rive animation |
video_url | video | Remote URL to an MP4 or HLS video stream |
image_url | image | Remote URL to a JPEG, PNG, WebP, or SVG file |
thumbnail_url | video | Optional poster image for the video block |
Any of these fields can also accept inline data where supported (e.g. Lottie supports inline JSON). The SDK auto-detects the format from the URL extension or the inline payload.
Rich media inside paywalls is configured per section. The console exposes section types like paywall_header, paywall_features, and paywall_cta, each of which can host an image, Lottie animation, Rive animation, or video. The Flutter SDK simply presents the paywall and the native renderer handles the rest:
try {
await AppDNA.paywall.present(
"premium_upgrade",
context: const PaywallContext(placement: "settings"),
);
} catch (e) {
print("Paywall not available: $e");
}
Common patterns:
- Hero Lottie — attach a Lottie URL to the
paywall_header section to play a looping product animation while the user reads features.
- Feature illustrations — inside
paywall_features, each feature row can carry an inline icon (Lucide / SF Symbols / Material / emoji) or a remote image.
- Video proof — a short looping product demo can be embedded into a section with
paywall_video style.
- CTA pulse — the
paywall_cta section supports CTA animations (pulse, glow, bounce) configured entirely from the Console.
Onboarding steps are composed of an ordered list of content blocks. Each block has a type and optional styling. Supported block types include:
| Block Type | Description |
|---|
heading | Styled heading (level 1-6) |
text | Body text with styling |
image | Remote image with corner radius and sizing |
button | Action button (next, skip, link, permission) |
spacer | Vertical spacing |
list | Bullet, numbered, or checklist |
divider | Horizontal separator |
badge | Colored badge label |
icon | Icon with emoji or icon reference |
toggle | Switch with label and description |
video | Inline video player |
lottie | Lottie animation |
rive | Rive animation |
Blocks are added in the Console — the Flutter SDK simply presents the flow:
try {
await AppDNA.onboarding.present("main_flow");
} catch (e) {
print("Flow config not available: $e");
}
In-app messages support a hero area at the top of the modal, banner, or fullscreen layout. The hero can host:
- An image (with optional GIF playback)
- A Lottie animation
- A short looping video
The rest of the message body uses the same content block system as onboarding, so you can mix icons, badges, headings, text, and CTAs underneath the hero.
In-app messages are triggered by event rules in the Console, but you can also gate display with an AppDNAInAppMessageDelegate:
class MyMessageObserver implements AppDNAInAppMessageDelegate {
@override
void onMessageShown(String messageId, String trigger) {
print("Message shown: $messageId");
}
@override
void onMessageAction(String messageId, String action) {
print("Message action: $action");
}
@override
void onMessageDismissed(String messageId) {
print("Message dismissed: $messageId");
}
@override
bool shouldShowMessage(String messageId) {
// Return false to suppress display.
return true;
}
}
AppDNA.inAppMessages.setDelegate(MyMessageObserver());
Animation Playback Control
All animation playback is configured per section / per content block in the AppDNA Console. The native renderers honor the data fields embedded in the config:
| Field | Applies to | Description |
|---|
autoplay | Lottie, Rive, video | Start playing as soon as the view is on screen |
loop | Lottie, Rive, video | Loop playback indefinitely |
speed | Lottie | Playback speed multiplier (e.g. 1.5) |
muted | Video | Mute audio |
show_controls | Video | Show the native player chrome |
play_on_tap | Lottie | Play once when the user taps the surface |
play_on_scroll | Lottie | Tie progress to scroll position |
artboard | Rive | Choose which artboard to display |
state_machine | Rive | Name of the state machine to drive |
These fields are wired automatically by the renderer — you do not need to forward them through any Dart bridge.
Local Asset Embedding
For shipping assets inside the app bundle (instead of downloading them at runtime), use platform-native asset locations:
- iOS — drop the
.json / .riv / video file into your iOS host project’s asset catalog or main bundle.
- Android — drop the asset into
app/src/main/assets/ (or res/raw/ for video).
- Console — reference the bundled asset with an
asset:// URL (e.g. asset://intro.json).
The native renderer resolves asset:// URLs against the host app bundle before falling back to network. Flutter assets declared in pubspec.yaml are not visible to the native renderers, so put bundled rich media in the iOS / Android native locations described above.
Fallback Behavior
The native renderers degrade gracefully when an asset cannot be loaded:
- Lottie / Rive — if the file is missing, malformed, or the network request fails, the block is skipped (no empty space, no crash). The next block in the layout takes its slot.
- Video — if the video URL is unreachable, the optional
thumbnail_url is shown instead. If no thumbnail is configured, the block is skipped.
- Image — if the image URL is unreachable, the block is skipped. There is no broken-image placeholder.
- Inline JSON Lottie — if the inline JSON is malformed, the block is skipped and a debug log is emitted.
All fallbacks are silent in release builds. In debug builds, the native SDKs emit warnings you can see in Xcode / Logcat.
- Lazy loading — rich-media blocks are loaded on demand. A Lottie / Rive / video asset is only fetched when its block enters the visible viewport.
- Asset caching — remote assets are cached by the native HTTP stack (URLCache on iOS, OkHttp disk cache on Android). Subsequent presentations reuse the cached file.
- Memory — the native renderers tear down decoded animations when the host view is dismissed, so animations do not accumulate memory across flows.
- Cold start — avoid placing large video files on the very first onboarding step. Use a Lottie or static image for the welcome step and put video on later steps so playback starts after configuration is fully loaded.
- Inline JSON — inline Lottie payloads add to the config bundle size. Prefer remote URLs for animations larger than ~50 KB to keep config refreshes fast.
Icons
The SDK supports structured icon references across all modules. Icons resolve to platform-native equivalents:
| Library | Description | Example |
|---|
lucide | Mapped to SF Symbols (iOS) / Material Icons (Android) | "heart" |
sf-symbols | Native SF Symbols on iOS; mapped to Material on Android | "star.fill" |
material | Native Material Icons on Android; mapped to SF Symbols on iOS | "favorite" |
emoji | Unicode emoji characters | "fire" |
Icons are used in onboarding content blocks, paywall feature lists, in-app message CTA buttons, and survey question options.
Visual Effects
Visual effects (haptics, particle effects, blur backdrops) are rendered by the native SDKs and configured in the Console. The Flutter SDK does not need any wiring — effects fire automatically based on the triggers attached to the section or step.
Haptic Feedback
Haptic feedback is triggered automatically based on Console configuration. Supported haptic types:
| Type | Description |
|---|
light | Subtle tap |
medium | Medium tap |
heavy | Strong tap |
selection | Selection change feedback |
success | Success notification |
warning | Warning notification |
error | Error notification |
Haptic triggers can be configured for:
- Step advances (onboarding)
- Button taps
- Plan selection (paywalls)
- Option selection (surveys, forms)
- Toggle changes
- Form submission
- Error and success states
Particle Effects
Particle effects overlay the current screen. Supported effect types:
| Type | Description |
|---|
confetti | Colorful falling confetti |
sparkle | Sparkling particles |
fireworks | Burst effect |
snow | Falling snowflakes |
hearts | Floating hearts |
Each effect has a trigger (on_appear, on_step_complete, on_purchase, on_flow_complete), duration, intensity (light, medium, heavy), and optional custom colors.
Blur / Glassmorphism
Blur backdrops use the native platform’s high-performance blur primitive (iOS UIVisualEffectView, Android RenderEffect) for performant glassmorphism effects:
- Radius — blur intensity
- Tint — overlay color tint
- Saturation — color saturation adjustment
Blur is supported as a backdrop for in-app message modals and fullscreen overlays.
Styling
All visual elements support fine-grained styling through the Console:
- Text styles — font family, size, weight, color, alignment, line height, letter spacing, opacity
- Backgrounds — solid color, linear/radial gradient, or remote image with overlay
- Borders — width, color, style, and per-corner radius
- Shadows — offset, blur, spread, and color
- Spacing — per-side padding and margin
- Animations — entry animations (slide, fade, scale), section stagger, CTA pulse/glow/bounce, dismiss animations
The native SDKs use platform-native fonts only. Custom web fonts are not supported. Specify system font families in the Console and the SDK will render them using each platform’s default typeface.
Dynamic Content Templates
Rich media content supports template interpolation using {{variable}} syntax. Available namespaces:
| Namespace | Example | Source |
|---|
user.* | {{user.userId}} | Identified user traits |
session.* | {{session.selected_plan}} | App-defined session data |
onboarding.* | {{onboarding.step1.name}} | Onboarding form responses |
computed.* | {{computed.custom_value}} | Data from async hooks |
device.* | {{device.platform}} | Device info (platform, OS, locale, country) |
remote_config.* | {{remote_config.app_name}} | Remote config values |
Use the pipe syntax for fallback values: {{user.name | there}} renders as "there" if the user name is not set.
No Dart Code Required
All rich media features are configured in the AppDNA Console. The Flutter SDK forwards a configuration ID to the native renderer, which handles all decoding, layout, and animation. You do not need to write any Dart code to use Lottie, Rive, video, haptics, particles, blur, icons, or content blocks.