How to Create an Atomic Loader in Jetpack Compose

Kappdev
4 min readSep 2, 2024

--

Welcome πŸ‘‹

In this article, we will create a stunning 3D Atomic Loader animation in Jetpack Compose.

Excited? 🀩 Let’s dive in! βš›οΈπŸš€

Created by Kappdev

Inspiration ✨

This loader animation is inspired by the original work created by Martin van Driel using HTML and CSS, which is licensed under the MIT License.

I recreated this effect using Jetpack Compose to bring it to Android. Enjoy the tutorial!

Creating the Rotating Circle

To start, we need to create a RotatingCircle composable function. This function will render a single circle, which we’ll use to compose the loader.

@Composable
fun RotatingCircle(
modifier: Modifier,
rotationX: Float,
rotationY: Float,
rotationZ: Float,
borderColor: Color,
borderWidth: Dp
) {
// Further implementation
}

Drawing the Circle

To achieve the desired effect, we subtract one circle from another and draw the result on a Canvas:

Canvas(modifier) {
// Define the path for the main circle
val mainCircle = Path().apply {
addOval(Rect(size.center, size.minDimension / 2))
}

// Adjust the position of the clipping circle to the left by borderWidth
val clipCenter = Offset(size.width / 2f - borderWidth.toPx(), size.height / 2f)
// Define the path for the clipping circle
val clipCircle = Path().apply {
addOval(Rect(clipCenter, size.minDimension / 2))
}

// Subtract the clipping circle from the main circle
val path = Path().apply {
op(mainCircle, clipCircle, PathOperation.Difference)
}

// Draw the subtracted path
drawPath(path, borderColor)
}
Subtraction

Transformation

Next, we use graphicsLayer to rotate the Canvas in a 3-dimensional space:

Canvas(
modifier.graphicsLayer {
this.rotationX = rotationX
this.rotationY = rotationY
this.rotationZ = rotationZ
// To create a depth effect adjust the cameraDistance
cameraDistance = 12f * density
}
) {
// Drawing logic
}

Composing the Loader

To render the loader, we define theAtomicLoader composable function:

@Composable
fun AtomicLoader(
modifier: Modifier,
color: Color = Color.White,
borderWidth: Dp = 3.dp,
cycleDuration: Int = 1000
)

Parameters

βš›οΈ modifier πŸ‘‰ Modifier to be applied to the loader container.

βš›οΈ color πŸ‘‰ Color of the loader.

βš›οΈ borderWidth πŸ‘‰ Width of the loader’s border.

βš›οΈ cycleDuration πŸ‘‰ Duration of one complete rotation cycle in milliseconds.

Creating the Rotation Animation

To animate the loader, we define an infinite rotation that cycles from 0 to 360 degrees:

val infiniteTransition = rememberInfiniteTransition("InfiniteAtomicLoaderTransition")

val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(cycleDuration, easing = LinearEasing)
),
label = "AtomicLoaderRotation"
)

Placing the Circles

Finally, we position the circles inside the Box composable and apply the rotation animation:

Box(modifier) {
RotatingCircle(
modifier = Modifier.matchParentSize(),
rotationX = 35f,
rotationY = -45f,
rotationZ = -90f + rotation,
borderColor = color,
borderWidth = borderWidth
)
RotatingCircle(
modifier = Modifier.matchParentSize(),
rotationX = 50f,
rotationY = 10f,
rotationZ = rotation,
borderColor = color,
borderWidth = borderWidth
)
RotatingCircle(
modifier = Modifier.matchParentSize(),
rotationX = 35f,
rotationY = 55f,
rotationZ = 90f + rotation,
borderColor = color,
borderWidth = borderWidth
)
}

Congratulations πŸ₯³! We’ve successfully built it πŸ‘. You can find the full code on GitHub Gist πŸ§‘β€πŸ’». Let’s explore the usage πŸ‘‡

Practical Usage πŸ’β€β™‚οΈ

Let’s create a Box with a radial gradient background and place the loader at the center to see the result.

⚠️ Ensure the loader has a clearly defined size! If not, it might not appear or could cause unexpected behavior.

Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.radialGradient(
listOf(Color(0xFF3C4B57), Color(0xFF1C262B))
)
),
contentAlignment = Alignment.Center
) {
AtomicLoader(
Modifier.size(100.dp)
)
}

Output 😍

Custom Loaders | Jetpack Compose

7 stories

Thank you for reading this article! ❀️ If you found it enjoyable and valuable, show your appreciation by clapping πŸ‘ and following Kappdev for more exciting articles 😊

--

--

Kappdev

πŸ’‘ Curious Explorer 🧭 Kotlin and Compose enthusiast πŸ‘¨β€πŸ’» Passionate about self-development and growth ❀️‍πŸ”₯ Push your boundaries πŸš€