
In this article, we’ll explore CircularProgressIndicator in Jetpack Compose with the help of examples.
Prerequisites:
What is a Circular Progress Indicator?
They act like loading spinners. They inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates.
There are two types of circular progress indicators – indeterminate and determinate.
1. Indeterminate CircularProgressIndicator:
It runs forever.

2. Determinate CircularProgressIndicator:
It is similar to the indeterminate indicator but shows progress value instead of running forever.

Let’s see how to implement them in the Android Studio.
First, create an empty Compose project and open MainActivity. Create a MyUI() composable and call it from the onCreate() method. We’ll write our code in it.
MainActivity for the Material 3 Jetpack Compose:
// add the following packages
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.draw.rotate
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyUI()
}
}
}
}
}
}
@Composable
fun MyUI() {
}
MainActivity for the Material 2 version:
// we'll use the following imports
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.Button
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ProgressIndicatorDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme(darkTheme = false) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyUI()
}
}
}
}
}
}
@Composable
fun MyUI() {
}
Note: I am using the Material 3 Jetpack Compose. If you are working with Material 2, you may see a slightly different appearance, but the code will still work as expected.
1. Indeterminate CircularProgressIndicator:
The API looks like this:
@Composable
fun CircularProgressIndicator(
modifier: Modifier = Modifier,
color: Color = ProgressIndicatorDefaults.circularColor,
strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth,
trackColor: Color = ProgressIndicatorDefaults.circularTrackColor,
strokeCap: StrokeCap = ProgressIndicatorDefaults.CircularIndeterminateStrokeCap,
)
modifier – It is to change the layout of the progress bar. For example, we set the size using the size() modifier.
color – Indicator’s foreground color.
strokeWidth – Indicator’s width (thickness).
trackColor – The background color. In Material 2, it is backgroundColor.
strokeCap – Stroke cap at the end of this progress bar. There are 3 values: StrokeCap.Butt (default), StrokeCap.Round, and StrokeCap.Square.
Example:
@Composable
fun MyUI() {
CircularProgressIndicator(
modifier = Modifier.size(size = 64.dp),
color = Color.Magenta,
strokeWidth = 6.dp
)
}
Output:

Related: How to Use Colors in Jetpack Compose?
Background Color:
We can change the background color using the trackColor (backgroundColor in Material 2) parameter.
@Composable
fun MyUI() {
CircularProgressIndicator(
modifier = Modifier.size(size = 64.dp),
color = Color.Magenta,
trackColor = Color.Magenta.copy(alpha = 0.4f),
strokeWidth = 6.dp
)
}
Output:

Adding Text:
We can easily display text using the Text() and Row() composables.
@Composable
fun MyUI() {
Row(verticalAlignment = Alignment.CenterVertically) {
CircularProgressIndicator(
modifier = Modifier.size(size = 28.dp),
color = Color.Red
)
Spacer(modifier = Modifier.width(width = 8.dp))
Text(text = "Loading...")
}
}
Output:

Related: 5 Simple Text Animations in Jetpack Compose
2. Determinate CircularProgressIndicator:
@Composable
fun CircularProgressIndicator(
progress: Float,
modifier: Modifier = Modifier,
color: Color = ProgressIndicatorDefaults.circularColor,
strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth,
trackColor: Color = ProgressIndicatorDefaults.circularTrackColor,
strokeCap: StrokeCap = ProgressIndicatorDefaults.CircularDeterminateStrokeCap,
)
The progress value ranges from 0f to 1f. 0f represents no progress, and 1f means 100% progress.
Example:
@Composable
fun MyUI() {
CircularProgressIndicator(
progress = 0.6f, // 60% progress
modifier = Modifier.size(size = 64.dp),
color = Color.Magenta,
strokeWidth = 6.dp
)
}
Output:

Adding Animation:
By default, you don’t see any animation while showing the progress. We can add a smooth animation by using animateFloatAsState API.
@Composable
fun MyUI() {
// progress value
var progress by remember {
mutableStateOf(0f) // initially 0f
}
// animation
val progressAnimate by animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
)
// this is called when the Activity is launched
LaunchedEffect(Unit) {
progress = 0.6f
}
CircularProgressIndicator(
progress = progressAnimate,
modifier = Modifier.size(size = 64.dp),
color = Color.Magenta,
strokeWidth = 6.dp
)
// add space between indicator and button
Spacer(modifier = Modifier.height(height = 16.dp))
// button
Button(
onClick = {
progress = (0 until 100).random().toFloat() / 100
}
) {
Text(
text = "Random",
color = Color.White
)
}
}
Output:

The distortion in the output is caused by the recorder, not the code.
Note: Instead of ProgressAnimationSpec, you can specify other animation spec functions.
By default, the indicator starts at the top center. We can change that by using the modifier’s rotate() function.
@Composable
fun MyUI() {
CircularProgressIndicator(
progress = 0.6f,
modifier = Modifier
.size(size = 64.dp)
.rotate(degrees = 45f),
color = Color.Magenta,
strokeWidth = 6.dp
)
}
Output:

The Progress Indicator API doesn’t offer many customizations. If you want to create your own indicator, use Canvas. Take a look at these beautiful custom progress bars made with Canvas.
This is all about CircularProgressIndicator in Jetpack Compose. I hope you have learned something new. If you have any doubts, comment below.
Related Articles:
References: