Learn how to have runtime control of all parts of an animation or image, the color, opacity, scale, rotation .. any property within your SVG animation using Lottie Dynamic Properties.
This tutorial is going to explain Lottie Dynamic Properties. Dynamic Properties are a way of controlling at runtime, the different parts of your animation. You could theme your animation based on user preferences, you could animate different parts of the animation based on user state/progress, or use the same animation file to represent multiple items where you control what is shown and how its shown.
If you don’t get it by now, simply, controlling properties of an animation adds flexibility and customisation to your apps without the overhead of multiple assets to download/manage.
Now before the step by step guide to using dynamic properties with a Lottie image, the reason I wrote this blog is because there is just one missing/unclear piece to the regular docs.
To control dynamic properties and understand KeyPaths (explained further down), the answer is not in the source json files but in the resolved in memory code. If you are reading this blog looking for an understanding of the keypath you need, use this code anywhere in your composition to print out the keypaths of your image and all will become clear:
AndroidView(
factory = { c ->
LottieAnimationView(c).apply {
setAnimationFromUrl("https://drive.google.com/uc?id=1ebWqd_e2ci4kSKB83e37q2Bl0YMadwxv")
addLottieOnCompositionLoadedListener {
Log.d("TUT", "Keypaths: ${resolveKeyPath(KeyPath("**"))}")
}
}
}
)
N.B you have to use an AndroidView to do this, as the resolveKeyPath()
method doesn’t currently exist on the Lottie composable api.
For the rest of us! Let’s go through the steps one by one to load a Lottie image and control one of its properties. The source code for all the snippets in this blog can be found at the demo repository here.
After adding the lottie dependencies (see demo project for full code):
implementation(libs.lottie)
implementation(libs.lottieCompose)
Creating a Lottie image from a url can be done like this:
val composition by rememberLottieComposition(
spec = LottieCompositionSpec.Url("https://drive.google.com/uc?id=1ebWqd_e2ci4kSKB83e37q2Bl0YMadwxv"),
)
LottieAnimation(
composition = composition,
iterations = LottieConstants.IterateForever,
enableMergePaths = true,
modifier = modifier,
)
Easy! This will load the Lottie image from the URL and display it inside whatever Composable you declare. Enable merge paths is experimental, pls read the docs, but I’ve found its better to have it on to support lottie images that use it and fix images that may look incorrect rather than have it off and have images not work that can’t avoid merge paths.
Now to control a dynamic property of a Lottie image, you need to use the LottieDynamicProperties
and pass it to your LottieAnimation
.
val dynamicProperties: LottieDynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(
property = LottieProperty.COLOR,
keyPath = arrayOf(
"lemon", "lemon-body-sunglasses", "**"
),
value = color.toArgb(),
),
)
LottieAnimation(
composition = composition,
iterations = LottieConstants.IterateForever,
enableMergePaths = true,
dynamicProperties = dynamicProperties,
modifier = modifier,
)
Controlling a dynamic property (controlling 1 aspect of the animation), requires a declaration of the property
to change, a keyPath
which is a lookup to where in the image layer contents needs to be manipulated (work with your designer for good, obvious naming of these layers), and the value
that you want to change it to.
Here we want to change the COLOR
property
, with the value
of color.toArgb()
, which is in the keyPath "lemon", "lemon-body-sunglasses", "**"
, as in, it is in a layer named lemon with a child layer named lemon-body-sunglasses, and then a glob **
for any content below that that can have its color manipulated.
I.e. this part of the example image:
You can also be very specific with your KeyPath such as: ["lemon", "big-leaf", "Big_leaf_00000052817416241482779350000003325489541389591972_", "Fill 1"]
but I find it more flexible to leave a bit of detail out, i.e. ["lemon", "big-leaf", "**"]
. This depends on your Lottie files details and agreements with the asset providers (designers).
That is basically it, the rest of the code in the demo application shows how to hook-up changes to the animation on the press of a button, and you can then imagine how to trigger changes based on anything you normally would change in the same fashion in your compose UI.
This blog showed the code for changing the sunglasses color, but the sample application also changes the leaf color and the opacity of the mouth for this animation.
The main gotcha with Dynamic Properties is:
Don’t try and lookup your keypath based on the source JSON file.
Lookup your keypaths after the image is loaded into memory and then log them out.
For example, if you incorrectly looked in the source file example here. It shows “lemon-body-sunglasses” under “assets1” inside “assets”:
However once you resolve the keypaths into memory (example file here) and log them out, you can see that “lemon-body-sunglasses” is actually under the “lemon” keypath:
That’s it! When I was first using Dynamic Properties I found the coding simple and intuitive, it was only the understanding of how to get the KeyPath for any part of the animated image that I couldn’t find an answer for.
Maybe someone else will understand straight from the official docs, (and this quote is me patching together some parts). But for ages it just wasn’t clear, and I wasn’t sure if it was me, or the assets that the designer I was working with was giving me :-). Now we all know!
Animation properties are stored in a data tree that mimics the information heirarchy of After Effects. In After Effects a
Composition
is a collection ofLayers
that each have their own timelines.Layer
objects have string names [“nm” in json], and their contents can be an image, shape layers, fills, strokes, or just about anything that is drawable. Each object in After Effects has a name [“nm” in json]. Lottie can find these objects and properties by their name using aKeyPath
.KeyPaths have the ability to store an internal reference to the content that they resolve to. When you create a new KeyPath object, it will be unresolved. LottieDrawable and LottieAnimationView has a
resolveKeyPath()
method that takes a KeyPath and returns a list of zero or more resolved KeyPaths that each resolve to a single piece of content and are internally resolved. This can be used to discover the structure of your animation if you don’t know it. To do so, in a development environment, resolvenew KeyPath("**")
and log the returned list.
Thanks for listening.
All code is available on GitHub here.
Any questions, you can find me on:
BlueSky @Blundell_apps (or Threads, or X, :shrug:)
Enjoy!
p.s. I apologise for the featured image, these AI’s are out of control!