Lottie ComposeはAirbnbが公開しているLottieのJetpack Compose向けAPIで、After Effectsなどから書き出したJSONアニメーションをコンポーザブルとして描画します。この記事では、Gradleへの追加から再生制御、読み込み中から成功表示への切り替え、.lottieファイルの扱い、再生されないときの原因までを、バージョン6.7.1のソースで確かめた引数名・既定値とともに整理します。
まとめ
Lottie Composeの導入は com.airbnb.android:lottie-compose を1行追加するだけで済みます。このアーティファクトは lottie 本体を api 依存で内包しているため、本体を別途書き足す必要はありません。最小構成は rememberLottieComposition でJSONを読み、LottieAnimation に渡す2手です。
時間を取られやすいのは機能追加ではなく挙動の解釈です。再生されない症状は、コードではなく端末設定が原因のことがあります。Lottieは既定で速度を端末のアニメータスケールで割るため、開発者オプションやバッテリーセーバーでスケールが0になった端末では、アニメーションが一瞬で最終フレームへ飛びます。
最新版は6.7.1(2025年10月31日公開)で、2026年11月時点まで1年近く新しいリリースは出ていません。.lottie(dotLottie)形式は1ファイル1アニメーションなら読めますが、ステートマシンやテーマは扱えません。Compose Multiplatformにも対応していないため、iOSやデスクトップと同じコードで動かしたい場合は別ライブラリを選ぶ判断になります。
Lottie Composeの守備範囲と6.7.1時点の依存関係
lottie-composeとlottie本体の依存関係
Lottie Composeは独立したライブラリではなく、Android版Lottieのレンダリングエンジンをそのまま使い、Compose向けのコンポーザブルとステート管理を被せたモジュールです。リポジトリの lottie-compose/build.gradle には api project(':lottie') と書かれており、依存は推移的に公開されます。
lottie-compose を追加すれば LottieComposition や LottieProperty といった本体側のクラスもそのまま参照できます。両方を明示的に書いているサンプルを見かけますが、バージョンがずれる事故を増やすだけなので不要です。
対応環境と6.7.1以降のリリース状況
ビルド設定上の対応範囲は minSdk 21、compileSdk 36、targetSdk 36 です。Android 5.0以降であれば動作対象に入ります。直近のリリースはlottie-androidのCHANGELOGに次のように記録されています。
| バージョン | 公開日 | 主な変更 |
|---|---|---|
| 6.7.1 | 2025-10-31 | 巨大なBitmap生成への保護 |
| 6.7.0 | 2025-10-27 | 3Dアニメーションの初期対応 |
| 6.6.10 | 2025-10-02 | RTL対応 |
| 6.6.9 | 2025-09-16 | MULTIPLYブレンド描画の修正(API 34+) |
Maven Centralのメタデータでも、2026年11月3日時点の最新は6.7.1のままです。GitHubのmasterには2026年2月15日に「ウィンドウごとのUIスレッドでの描画対応」が入っていますが、リリースには含まれていません。この記事のAPIと既定値は6.7.1のソースで確かめたものです。版を上げるときはCHANGELOGで該当する引数の変更有無を見てから更新してください。なお公式ドキュメントのURLは airbnb.io/lottie から lottie.airbnb.tech へ301リダイレクトされます。
Gradleへの追加とバージョンカタログでの管理
バージョンカタログを使う構成では、libs.versions.toml に次のように書きます。
[versions]
lottie = "6.7.1"
[libraries]
lottie-compose = { module = "com.airbnb.android:lottie-compose", version.ref = "lottie" }
モジュール側の build.gradle.kts では1行です。
dependencies {
implementation(libs.lottie.compose)
}
Lottie Composeの版番号はCompose BOMとは別体系で、BOMの版に合わせて選ぶ必要はありません。6.7.1のPOMは compose-bom 2024.02.01とKotlin標準ライブラリ1.9.22を参照しています。Gradleは同じライブラリの版が衝突すると新しい方を採るため、これより新しいBOMを使うアプリではアプリ側のCompose版がそのまま使われ、古いComposeを固定しているアプリだけがLottieの追加で2024.02.01相当へ引き上げられます。BOMの運用そのものはJetpack Composeとは?宣言的UIの仕組みとBOM運用・採用判断を解説で扱っています。
アニメーションの読み込みと表示
LottieCompositionSpecが受け取れる6種類の入力元
読み込み元は LottieCompositionSpec というsealed interfaceで表現され、実装は6つのvalue classです。RawRes(res/rawのリソースID)、Asset(assetsのファイル名)、Url(HTTP経由)、File(端末上のファイルパス)、JsonString(JSON文字列そのもの)、ContentProvider(Uri)です。
実務でまず使うのは RawRes です。ビルド時にリソースIDへ解決されるため、ファイル名のタイプミスがコンパイルエラーになります。サーバーから差し替えたい場合だけ Url を選びます。
@Composable
fun LoadingAnimation() {
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.loading)
)
LottieAnimation(
composition = composition,
iterations = LottieConstants.IterateForever,
modifier = Modifier.size(160.dp),
)
}
LottieConstants.IterateForever の実体は Integer.MAX_VALUE で、無限ループではなく「事実上終わらない回数」の指定です。途中で止めたい場合は後述の再生制御を併用します。
.lottie(dotLottie)・ZIP・tgsファイルを読み込む条件
6.6.0で解析処理がZIP前提に統一され、JSON以外にZIP、.lottie(中身はZIP)、Telegramスタンプの.tgs(中身はgzip圧縮JSON)も読めます。ただし形式の判定方法が入力元ごとに違います。
RawRes・Asset・ContentProvider:ファイル先頭のマジックバイトでZIPかgzipかを判定する。拡張子は問わない。Url:Content-Typeがapplication/zip系か、URL末尾が.lottieならZIPとして読む。File:ファイル名の末尾がzipかtgsかだけで分岐し、それ以外はJSONとして読む。
落とし穴は File です。ダウンロードした anim.lottie をそのままのファイル名で渡すとJSONとして解析され、読み込みに失敗します。保存時に拡張子を .zip にするか、ContentProvider で渡してください。
もう1つの制約はZIPの中身の扱いです。LottieCompositionFactoryの実装は manifest.json を読み飛ばし、ZIP内で .json を見つけるたびに解析結果を上書きします。アニメーションを1本だけ含む.lottieなら問題ありませんが、複数のアニメーションやテーマ・ステートマシンの定義を詰めた.lottieでは、どのJSONが残るかを制御できません。そうしたファイルを使う場合は後述のdotlottie-androidを選びます。
res/raw-nightによるダークモード用アニメーションの切り替え
ライト用とダーク用で別のJSONを用意しているなら、res/raw/loading.json と res/raw-night/loading.json に同名で置くだけで切り替わります。RawRes のキャッシュキーは rawRes_night_<id> と rawRes_day_<id> に分かれているため、昼の解析結果が夜に使い回されることはありません。既定ではダークモードの切り替えでActivityが作り直され、rememberLottieComposition が夜のリソースを読み直します。
この方法が効かない構成は2つあります。1つは、マニフェストの android:configChanges に uiMode を入れてActivityを作り直さない場合です。rememberLottieComposition は spec と cacheKey だけをキーに結果を保持するため、構成が夜に変わっても読み込み済みの昼のアニメーションが表示され続けます。もう1つは、アプリ内のテーマ切り替えをComposeの状態だけで持ち、構成の uiMode を変えていない場合で、raw-night 自体が選ばれません。色を変えるだけなら、JSONを2本持つより後述の動的プロパティでテーマ色を流し込む方が管理が楽です。
LottieAnimationの2系統のオーバーロードの使い分け
LottieAnimation の宣言はソース上4本ありますが、うち1本は @Deprecated("Pass progress as a lambda instead of a float. This overload will be removed in the next release.")、もう1本は DeprecationLevel.HIDDEN のバイナリ互換用です。実際に選ぶのは2系統に絞られます。
progress: () -> Floatを受け取る版:進捗を呼び出し側が管理する。ジェスチャーやダウンロード進捗に連動させる場合はこれ。isPlayingやiterationsを直接受け取る版:内部で駆動まで面倒を見る。単純な再生だけならこれで足ります。
進捗がラムダである点には理由があります。値で受け取ると毎フレームの進捗変化がコンポーズ関数の再実行を招きますが、ラムダなら読み取りが描画フェーズまで遅延され、再コンポーズを回避できます。CHANGELOG_COMPOSEの5.2.0の項に、この変更が破壊的変更として記録されています。
読み込み中と失敗時の状態分岐
戻り値の LottieCompositionResult は State<LottieComposition?> を継承しつつ isLoading、isFailure、isSuccess、error を公開しています。by で受けると中身の LottieComposition? だけが取れるので、状態を見たいときは委譲せずに受け取ります。
val result = rememberLottieComposition(
LottieCompositionSpec.Url("https://example.com/anim/success.json")
)
when {
result.isLoading -> CircularProgressIndicator()
result.isFailure -> Text("アニメーションを読み込めませんでした")
else -> LottieAnimation(
composition = result.value,
iterations = LottieConstants.IterateForever,
)
}
ネットワーク読み込みでは onRetry も使えます。型は suspend (failCount: Int, previousException: Throwable) -> Boolean、既定値は常に false(再試行しない)です。無条件に true を返すと再試行が止まらなくなるため、上限回数の判定を必ず入れてください。ユーザー操作で再試行させるなら rememberLottieRetrySignal を組み合わせます。
解析結果のキャッシュ上限とcacheKey
解析済みの LottieComposition はメモリ上のLRUキャッシュに保持され、上限は20件です。cacheKey を省略すると入力元から自動でキーが作られ、同じプロセス内でキャッシュから追い出されていなければ、画面を開き直しても再解析は走りません。一覧画面などで20種類を超えるアニメーションを行き来するなら LottieCompositionFactory.setMaxCacheSize() で上限を上げ、サーバー側で同じURLの中身を差し替えたときは LottieCompositionFactory.clearCache(context) でメモリとネットワークの両方のキャッシュを捨てます。キャッシュさせたくない一時的なアニメーションには cacheKey = null を渡します。
再生制御とユーザー操作への連動
animateLottieCompositionAsStateの主要な引数
再生の細かい制御は animateLottieCompositionAsState が担います。戻り値の LottieAnimationState は State<Float> なので、by で進捗の Float として受け取れます。
val composition by rememberLottieComposition(
LottieCompositionSpec.Asset("success.json")
)
val progress by animateLottieCompositionAsState(
composition = composition,
speed = 1.5f,
iterations = 3,
reverseOnRepeat = true,
)
LottieAnimation(composition = composition, progress = { progress })
reverseOnRepeat を true にすると、繰り返しのたびに逆再生されて往復します。restartOnPlay の既定は true で、isPlaying が false から true へ戻ったときに先頭まで巻き戻る挙動です。途中から再開したいなら false を指定してください。
LottieClipSpecによる区間・マーカー指定
アニメーション全体ではなく一部だけを再生したい場合は LottieClipSpec を渡します。サブクラスは4つあり、内部ではいずれも進捗の最小値・最大値へ変換されます。
| 指定方法 | 引数 | 用途 |
|---|---|---|
| LottieClipSpec.Frame | min / max / maxInclusive | フレーム番号で区切る |
| LottieClipSpec.Progress | min / max(0f〜1f) | 割合で区切る |
| LottieClipSpec.Markers | min / max / maxInclusive | 2つのマーカー名で挟む |
| LottieClipSpec.Marker | marker | 1つのマーカー区間だけ再生 |
デザイナーがマーカー名を維持してくれる取り決めがあるなら、マーカー指定を選びます。フレーム番号で書くと、アニメーションを作り直すたびにコード側の数値を直すことになります。区間が1つに収まるなら Marker、開始と終了を別々のマーカーで挟むなら Markers です。
スライダーやスクロールに進捗を連動させる実装
インタラクティブな挙動は、専用APIではなく進捗を自分で渡すことで実現します。progress のラムダに、任意のステートを返すだけです。
var sliderValue by remember { mutableFloatStateOf(0f) }
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.progress_bar)
)
Column {
LottieAnimation(composition = composition, progress = { sliderValue })
Slider(value = sliderValue, onValueChange = { sliderValue = it })
}
スクロール連動も同じ形で、LazyListState の値を0f〜1fへ正規化して返します。操作中はスライダー、指を離したら自動再生、のように2つの進捗を切り替える実装は避けてください。animateLottieCompositionAsState の進捗はスライダーと無関係に進んでいるので、切り替えた瞬間に表示が飛びます。操作と自動再生を行き来させるなら、後述の LottieAnimatable で snapTo と animate を使い、1つの進捗を引き継ぎます。
LottieAnimatableによるコルーチンからの逐次制御
「AをX回再生したあとBへ切り替える」といった逐次的な制御には rememberLottieAnimatable を使います。animate は suspend 関数なので、完了を待ってから次の処理へ進めます。suspend関数の待ち方やキャンセルの伝わり方はKotlin Coroutinesとは?suspendの仕組みと構造化並行性・採用判断で解説しています。
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.check)
)
val animatable = rememberLottieAnimatable()
LaunchedEffect(composition) {
animatable.animate(composition, iterations = 1)
}
LottieAnimation(
composition = animatable.composition,
progress = { animatable.progress },
)
描画に渡すのが animatable.composition である点が要点です。LottieAnimatable は排他制御のため、進行中のアニメーションを CancellationException で打ち切ってから次を開始します。開始が同期的とは限らず、animate を呼んだ直後に新しいcompositionを直接描画するのは安全ではありません。KDocも「常に LottieAnimationState.composition と LottieAnimationState.progress に依拠せよ」と明記しています。
画面の状態に合わせて再生を切り替える実装
読み込み中から成功・失敗の区間への切り替え
1本のJSONに「loading」「success」「error」のマーカーを打ってもらい、画面の状態に応じて区間を差し替える構成がよく使われます。LaunchedEffect のキーに状態を入れると、状態が変わるたびに前の animate が打ち切られて次の区間が始まります。
sealed interface SubmitState {
data object Loading : SubmitState
data object Success : SubmitState
data object Error : SubmitState
}
@Composable
fun SubmitAnimation(state: SubmitState) {
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.submit)
)
val animatable = rememberLottieAnimatable()
LaunchedEffect(composition, state) {
when (state) {
SubmitState.Loading -> animatable.animate(
composition,
clipSpec = LottieClipSpec.Marker("loading"),
iterations = LottieConstants.IterateForever,
cancellationBehavior = LottieCancellationBehavior.OnIterationFinish,
)
SubmitState.Success -> animatable.animate(
composition,
clipSpec = LottieClipSpec.Marker("success"),
iterations = 1,
)
SubmitState.Error -> animatable.animate(
composition,
clipSpec = LottieClipSpec.Marker("error"),
iterations = 1,
)
}
}
LottieAnimation(
composition = animatable.composition,
progress = { animatable.progress },
)
}
読み込み中のループに LottieCancellationBehavior.OnIterationFinish を指定しているのが要点です。既定の Immediately のままだと、通信が終わった瞬間にループの途中から成功区間の先頭へ飛び、つなぎ目が見えます。LottieAnimatableの実装では OnIterationFinish のとき再生を NonCancellable で包み、キャンセルされても現在の周回を最後まで描いてから抜けます。そのため成功表示は最大でループ1周分遅れます。コードで保証できるのは「周回の終わりまで待つ」ところまでで、つなぎ目が自然に見えるかは素材次第です。ループ区間の最終フレームと成功区間の最初のフレームが同じ絵になるように作ってもらい、1周が長い場合はループ区間を短くしてもらいます。
タップで切り替わるいいねボタン
ハートが満ちるアニメーションの最初のフレームを「未選択」、最後のフレームを「選択済み」として使えば、Lottieだけでトグルボタンを作れます。選択時は再生し、解除時は先頭へ戻すだけです。
@Composable
fun LikeButton(liked: Boolean, onLikedChange: (Boolean) -> Unit) {
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.heart)
)
val animatable = rememberLottieAnimatable()
LaunchedEffect(composition, liked) {
if (liked) {
animatable.animate(composition, iterations = 1)
} else {
animatable.snapTo(composition, progress = 0f)
}
}
LottieAnimation(
composition = animatable.composition,
progress = { animatable.progress },
modifier = Modifier
.size(48.dp)
.toggleable(
value = liked,
role = Role.Checkbox,
onValueChange = onLikedChange,
)
.semantics { contentDescription = "いいね" },
)
}
clickable ではなく toggleable を使うと、TalkBackが「チェックボックス、オン」のように状態を読み上げます。semantics の contentDescription で何を切り替えるボタンかを名前として与えます。本番では文字列をリソースに移してください。なおこの実装は、画面の再生成で liked = true のまま復元されると再生し直します。初回表示で再生させたくない場合は、最初の1回だけ snapTo(composition, progress = 1f) で最終フレームに合わせます。
動的プロパティによる色の差し替え
JSONを編集せずに描画内容を変えたい場合は rememberLottieDynamicProperties を使います。ダークテーマ対応や、テーマカラーへの追随はこれで賄えます。
val dynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(
LottieProperty.COLOR,
MaterialTheme.colorScheme.primary.toArgb(),
"**", "Fill 1",
),
)
LottieAnimation(
composition = composition,
dynamicProperties = dynamicProperties,
iterations = LottieConstants.IterateForever,
)
第3引数以降は可変長のキーパスで、"**" は階層のワイルドカードです。上の例は「どの階層にあっても Fill 1 という名前の塗り」を対象にします。プロパティは型が分かれており、COLOR と STROKE_COLOR は Integer(ARGB)、COLOR_FILTER は ColorFilter、GRADIENT_COLOR は Integer[] です。LottieDynamicProperties は渡された値の型を見て振り分けるため、型が合わないと反映されないまま何のエラーも出ないことがあります。
キーパスに使う名前はAfter Effects側のレイヤー名です。デザイナーがレイヤー名を変えると静かに壊れるので、差し替え対象のレイヤーは命名を固定する取り決めを先に作っておくと安全です。デザインツール側でLottieを書き出す流れはFigma Motionとは?使い方・書き出し・無料でできる範囲を解説【2026年10月】で扱っています。
描画設定とパフォーマンスの判断基準
RenderMode.AUTOMATICがソフトウェア描画へ切り替わる条件
renderMode の既定値は RenderMode.AUTOMATIC です。HARDWARE と SOFTWARE を明示した場合はその指定がそのまま通り、AUTOMATIC のときだけ次の条件判定が働いてソフトウェア描画が選ばれます。
- 破線パターンを含み、かつAPIレベル28未満(ハードウェア描画が破線に未対応だった世代)
- マスクとマットの合計が4を超える
- APIレベル25以下(該当端末でのクラッシュ報告が多いため)
実務で効くのは2番目です。マスクやマットを5個以上使ったアニメーションを渡すと、それだけでソフトウェア描画に落ちます。ソースのコメントも「いくつかのアニメーションを試した上でやや恣意的に決めた」と書いており、絶対的な基準ではありません。描画が重いと感じたら RenderMode.HARDWARE を明示して実機で比較してください。ただしAPIレベル25以下を対象に含むアプリで一律にHARDWAREを指定するのは避けます。この分岐はクラッシュ回避のために入っています。
AsyncUpdatesの既定値と実験的位置付け
asyncUpdates は AUTOMATIC、ENABLED、DISABLED の3値を取り、有効時は全ノードの進捗計算(setProgress)をメインスレッドの外へ逃がします。
見落とされやすいのが既定値の中身です。LottieAnimation の既定は AUTOMATIC ですが、そのJavadocには「この機能が枯れるまでは DISABLED として振る舞う」と明記されています。既定のままでは非同期更新は働かず、使うには AsyncUpdates.ENABLED を明示します。ノード数の多いアニメーションでフレーム落ちが出ているなら試す価値はありますが、API自体が実験的である旨もソースに書かれており、単純なローディング表示に先回りで指定するものではありません。
rememberLottiePainterをImageやIconに渡す場合の制約
6.3.0から、Lottieを Painter として扱う rememberLottiePainter が使えます。Image や Icon、Modifier.paint など、Painterを受け取る場所ならどこにでも置けるので、ボタン内のアイコンをアニメーションにしたい場面で便利です。
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.bell))
val progress by animateLottieCompositionAsState(composition)
Icon(
painter = rememberLottiePainter(composition = composition, progress = progress),
contentDescription = "通知",
tint = Color.Unspecified,
modifier = Modifier.size(24.dp),
)
ただし LottieAnimation と違い、progress はラムダではなく Float で受け取ります。進捗を読むのが呼び出し側のコンポーズ関数になるため、再生中は毎フレーム再コンポーズが走ります。画面全体のアニメーションや一覧の各行では再コンポーズの範囲と回数が増えるので、LottieAnimation を使います。Icon に渡すときは2点に注意します。Icon は既定で LocalContentColor の単色で塗りつぶすため、tint = Color.Unspecified を渡さないとアニメーションの配色が消えます。またLottiePainterの固有サイズはcompositionの幅と高さ(ピクセル)なので、Icon の既定の24dpは読み込み前にしか効かず、読み込み後はcompositionの大きさに広がります。サイズは Modifier.size で明示してください。
本番に残してはいけないデバッグ用引数
outlineMasksAndMattes はマスクとマットを輪郭表示するデバッグ用の引数で、オーバーヘッドが両者の面積に比例して増えるため、KDocに「本番で有効のままにしないこと」と大文字で書かれています。applyOpacityToLayers も処理コストが高いので、半透明シェイプの重なりが実際に破綻している画面だけで有効にします。
再生されない・崩れるときの原因切り分け
端末のアニメータスケール0による即時終了
コードは正しいのにアニメーションが動かない、あるいは一瞬で終わる場合、まず疑うのは端末設定です。animateLottieCompositionAsStateの実装では、実際の速度が次のように計算されています。
// Dividing by 0 correctly yields Float.POSITIVE_INFINITY here.
val actualSpeed = if (ignoreSystemAnimatorScale) speed
else (speed / Utils.getAnimationScale(LocalContext.current))
既定では端末のアニメータスケールで速度を割ります。開発者オプションの「アニメータ再生時間スケール」をオフにしている端末や、バッテリーセーバーが動作している端末ではスケールが0になり、実装コメントのとおり速度が Float.POSITIVE_INFINITY になります。結果として最終フレームまで一瞬で飛び、静止画に見えます。
切り分けには ignoreSystemAnimatorScale = true を渡します。KDocには「無視したければ false に設定」と書かれていますが、上の実装のとおり true のときにスケールを掛けない分岐へ入ります。食い違っているので実装側に従ってください。ただし本番アプリで一律に true を指定するのは推奨しません。ユーザーがアニメーションを切っているのに再生し続けることになります。
reduced motionマーカーによる静止表示への切り替え
OS側でアニメーションを減らす設定が有効な利用者には、再生を強行せず静止表示へ落とします。JSONに「reduced motion」という名前のマーカーを置いておくと、アニメーションが無効化された環境では、渡された進捗を無視して静止表示になります。マーカー名は大文字小文字を区別せず、reduced motion、reduced_motion、reduced-motion、reducedmotion の4通りが認識されます。
ただしComposeでは静止する位置に注意が要ります。6.7.1のLottieAnimationの描画処理は、マーカーの開始フレーム番号(30など)を0〜1の進捗としてそのまま drawable.progress に代入しています。1を超えた値は最終フレームに丸められるため、マーカーが0フレーム目にあれば先頭の絵、それ以外の位置なら最終フレームの絵で止まります。View版の LottieDrawable はフレーム番号として扱うので、同じJSONでもViewとComposeで静止する絵が変わります。Composeで静止させたい絵は、0フレーム目か最終フレームに置いてもらうのが確実です。
なお LottieAnimatable.animate には ignoreSystemAnimationsDisabled という引数があり、KDocには「OSでアニメーションが無効でも再生する」と書かれています。しかし6.7.1の実装では宣言されているだけで、処理の中から一度も参照されていません。指定しても挙動は変わらず、前項のスケール0の症状も解消しません。
画像アセット付きJSONでのimageAssetsFolder未指定
ビットマップを含むアニメーションでは、JSONと同じ場所に画像を置いただけでは読み込まれません。rememberLottieComposition の imageAssetsFolder は既定値が null で、指定しない限りassetsからの画像読み込みはスキップされます。assets配下のフォルダ名を明示的に渡すか、画像ごとZIPか.lottieにまとめて渡してください。ZIP内の画像はファイル名で自動的に結び付けられます。
テキストレイヤーのフォント解決とfontMap
フォント側の既定値は逆に設定済みで、fontAssetsFolder は "fonts/"、fontFileExtension は ".ttf" です。assets直下の fonts フォルダにAfter Effectsのフォントファミリー名と一致する名前で .ttf を置けば自動で解決されます。otfやアプリ内の既存Typefaceを使いたい場合は、LottieAnimation の fontMap にキーとTypefaceの対応表を渡します。キーはLottieファイル内の fName、fFamily、fFamily-fStyle のいずれかです。段落テキストとして境界ボックスが設定されているレイヤーなら、clipTextToBoundingBox = true で高さ方向にはみ出した分を描画しないようにできます。
特定端末のクラッシュに対するsafeMode
safeMode = true を指定すると描画処理が try/catch で包まれ、例外時はクラッシュせず空フレームが描画されます。原因調査が終わるまでの応急処置であり、例外は握り潰されるので恒久対策にはしません。
Lottie Composeで足りない場合の別ライブラリ
Compose Multiplatformで共通化するCompottie
Lottie ComposeはAndroid専用です。lottie-compose のビルド設定は com.android.library プラグインで構成されており、Kotlin Multiplatformのターゲットを持たないため、androidMain 以外から参照できません。JSONファイル自体はプラットフォーム非依存なので、iOSはlottie-ios、Webはlottie-webという分担であれば成立します。共通UIの考え方はCompose Multiplatformとは?KMPとの違いとiOS対応の現在地・採用判断を解説にまとめています。
共通コードで完結させたい場合の選択肢がCompottieです。v2.0からプラットフォームの実装に委譲せず、Kotlinで書かれた独自レンダラで描画しており、Android・iOS・JVM・macOS・Webに対応します。最新は2.3.3(2026年10月5日)で、エクスプレッション対応を外して容量を約半分にした compottie-lite、dotLottieとZIPを読む compottie-dot などモジュールが分かれています。独自レンダラである以上、After Effectsの全機能が再現される保証はないので、使いたい表現がリポジトリの対応表に含まれるかを先に確認してください。
ステートマシンやテーマを使うdotlottie-android
.lottieのステートマシン(タップやホバーで状態が遷移する定義)やテーマ切り替えをそのまま使いたい場合は、LottieFilesが公開しているdotlottie-androidが対象です。Rust製のdotlottie-rsで描画し、Compose向けに DotLottieAnimation を提供します。GitHubのリリースは0.17.0(2026年10月28日)ですが、READMEの導入例は0.5.0のまま更新されておらず、READMEが案内する座標 com.lottiefiles:dotlottie-android は2026年11月3日時点でMaven Centralに見つかりませんでした。JitPackのビルドAPIでは0.17.0が成功扱いになっているので、JitPackのリポジトリを追加して版番号を確認してから指定してください。
Android単体のアプリで、JSONか単一アニメーションの.lottieを再生するだけなら、Lottie Composeを使うのが妥当です。View版の LottieAnimationView と同じ LottieDrawable で描くため、既存のView実装から移行しても描画エンジンは変わりません。使う素材の表現が描画できるかは、どのライブラリでも実機で確かめてから採用してください。アニメーション形式そのものを選び直す段階であれば、RiveとLottieの違いを比較|料金・ランタイムサイズ・Unity対応・相互変換で選ぶ【2026年10月】で、料金やランタイムサイズの観点から比較しています。アニメーションを含むAndroidアプリの設計から任せたい場合は、弊社のAndroidアプリ開発でご相談を受け付けています。
よくある質問
lottie-composeを追加するとき、lottie本体も書く必要はありますか?
不要です。implementation("com.airbnb.android:lottie-compose:6.7.1") の1行だけで本体側のクラスも参照できます。Lottieの版番号はCompose BOMとは別体系なので、BOMの版に合わせて選ぶ必要もありません。
LottieAnimationで無限ループさせるにはどう書きますか?
iterations = LottieConstants.IterateForever を渡します。停止と再開を切り替えたい場合は isPlaying をステートで制御してください。既定では再開時に先頭へ巻き戻るため、途中から続けるなら restartOnPlay = false を併せて指定します。
.lottieファイルはLottie Composeで再生できますか?
アニメーションが1本だけの.lottieなら、RawRes や Asset にそのまま渡して再生できます。File で渡す場合は拡張子で形式を判定するため、ファイル名を .zip にしてください。ステートマシンやテーマを含む.lottieを使うならdotlottie-androidを選びます。
OSでアニメーションを減らす設定をしている利用者にはどう対応しますか?
JSONに reduced motion という名前のマーカーを置き、静止表示にします。6.7.1のComposeではマーカーを0フレーム目に置くと先頭の絵、それ以外に置くと最終フレームの絵で止まります。ローディング表示のように動きで処理中を伝えている画面は、静止した絵だけでは状態が伝わらないので、「読み込み中」のテキストやプログレス表示を併せて出します。ignoreSystemAnimationsDisabled は6.7.1では効かない引数なので、再生の強制には使えません。
iOSやデスクトップでも同じコードを使えますか?
使えません。Lottie ComposeはAndroidライブラリで、Compose Multiplatformの共通コードからは参照できません。共通化するならCompottieを使い、使いたい表現が独自レンダラの対応表に含まれるかを先に確認します。