Blog Infos
Author
Published
Topics
, , , ,
Published

 

Introduction

A pulse indicator is a simple but powerful UI element that helps visualize connectivity or activity status. Unlike a loading spinner, it conveys the idea of a signal radiating from a center point, which is especially useful for GPS or network connection states.

In this article we will look at a ready-to-use implementation in Jetpack Compose. The code is minimal, efficient, and easy to integrate into your app. By the end, you will see exactly how it works and how it looks in a real application.

Result preview

Before diving into the code, here is how the pulse indicator looks in action:

 

The Code

Here is the full composable function that renders the pulse indicator:

@Composable
private fun PulseIndicator(
    @DrawableRes icon: Int,
    modifier: Modifier = Modifier
) {
    val periodMs = 3600L
    val offsetsMs = longArrayOf(0L, 1200L, 2400L)

    val startNs = remember { System.nanoTime() }
    var frameTimeNs by remember { mutableLongStateOf(startNs) }

    LaunchedEffect(Unit) {
        while (true) {
            withFrameNanos { now -> frameTimeNs = now }
        }
    }

    fun phase(offsetMs: Long): Float {
        val elapsedMs = (frameTimeNs - startNs) / 1_000_000L + offsetMs
        return ((elapsedMs % periodMs).toFloat() / periodMs.toFloat())
    }

    Box(modifier.size(80.dp), contentAlignment = Alignment.Center) {
        @Composable
        fun Ring(p: Float) = Box(
            Modifier
                .matchParentSize()
                .graphicsLayer {
                    scaleX = 1f + 0.8f * p
                    scaleY = 1f + 0.8f * p
                    alpha = 1f - p
                }
                .border(1.5.dp, Color.White.copy(alpha = 0.9f), CircleShape)
        )

        Ring(phase(offsetsMs[0]))
        Ring(phase(offsetsMs[1]))
        Ring(phase(offsetsMs[2]))

        Box(
            Modifier
                .size(80.dp)
                .background(Color.White, CircleShape),
            contentAlignment = Alignment.Center
        ) {
            Image(
                painter = painterResource(icon),
                contentDescription = null,
                modifier = Modifier.size(32.dp)
            )
        }
    }
}
How It Works

Animation timing
The composable uses withFrameNanos inside a LaunchedEffect. This gives access to the current frame timestamp and ensures the animation runs smoothly while the composable is on screen. When it leaves composition, the coroutine is automatically cancelled.

Phase calculation
The function phase(offsetMs) converts the elapsed time into a value between 0f and 1f. Each ring has a different offset (012002400 ms), so they expand at different moments. This creates the illusion of continuous waves.

Ring rendering
Each ring is drawn as a Box with a circular border. Its size and opacity are modified using graphicsLayer:

  • scaleX and scaleY gradually grow from 1f to 1.8f.
  • alpha fades out from 1f to 0f.
    Together, this creates an expanding, fading circle.

Core icon
In the center, a solid white circle holds the provided icon (for example, a location pin). This acts as the static anchor point while the animated rings pulse outward.

 

How It Looks in a Real App

When you use this indicator in a screen that represents GPS connection, you get a clear and intuitive visual. For example:

  • While trying to connect, the background can be gray, and the pulse shows activity.
  • Once connected, the background turns into an active gradient, and the pulse continues around the location pin.

This gives users immediate feedback without needing to read extra text.

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Kobweb:Creating websites in Kotlin leveraging Compose HTML

Kobweb is a Kotlin web framework that aims to make web development enjoyable by building on top of Compose HTML and drawing inspiration from Jetpack Compose.
Watch Video

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kobweb

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author o ...

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kob ...

Jobs

Conclusion

The PulseIndicator is a lightweight and reusable Jetpack Compose component. It uses a single animation source (withFrameNanos), three rings with time offsets, and a central icon. The result is a smooth pulse effect that communicates connection status much better than a generic loading spinner.

You can drop this composable into any Compose screen and instantly provide a modern, engaging visual indicator for GPS, Bluetooth, or any other type of connectivity.

If you found this article useful, consider following me here on Medium for more practical Android development content.

You might also like:

Anatolii Frolov
Senior Android Developer
Writing honest, real-world Kotlin & Jetpack Compose insights.
📬 Follow me on Medium

This article was previously published on proandroiddev.com

Menu