Jetpack Compose Progress Indicator (ProgressBar): A Complete Guide
In Android development, providing feedback to users about ongoing processes is an essential part of creating a smooth and engaging user experience. Progress indicators, commonly known as progress bars, help users understand that an operation is in progress and that they should wait for it to complete. Jetpack Compose, the modern UI toolkit for Android, offers a powerful and easy-to-use way to implement progress indicators.
In this article, we will explore how to use progress indicators in Jetpack Compose, including the different types of progress bars, customization options, and common use cases.
What is a Progress Indicator?
A progress indicator is a visual element that shows the progress of an ongoing operation. It can either display the percentage of progress (like a horizontal bar) or show an indeterminate animation when the progress cannot be quantified (like a circular spinner).
In Jetpack Compose, the progress indicator is represented by two main components:
- Linear Progress Indicator – A horizontal progress bar.
- Circular Progress Indicator – A rotating circle indicating that the operation is ongoing.
Types of Progress Indicators in Jetpack Compose
Jetpack Compose provides two main types of progress indicators:
- Indeterminate Progress Indicator: This type is used when you don’t know how long the operation will take (e.g., waiting for a network response).
- Determinate Progress Indicator: This type is used when the progress can be quantified (e.g., file download progress).
1. Indeterminate Progress Indicator
Indeterminate progress indicators are useful when the exact progress is not known. These indicators are typically displayed as spinning circles or animations.
Example: Circular Indeterminate Progress Indicator
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun CircularIndeterminateProgress() {
Box(
modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center)
) {
CircularProgressIndicator()
}
}
@Preview(showBackground = true)
@Composable
fun PreviewCircularIndeterminateProgress() {
CircularIndeterminateProgress()
}
Key Points:
- The
CircularProgressIndicator()
is the main composable used for showing a circular spinner. - You can use the
Modifier
to control the size, padding, and alignment of the progress indicator. - This type of indicator runs an infinite animation, making it suitable for scenarios where the progress can’t be determined.
Customization of Circular Progress Indicator:
You can customize the CircularProgressIndicator
by passing parameters like color
and strokeWidth
.
CircularProgressIndicator(
color = MaterialTheme.colorScheme.primary,
strokeWidth = 8.dp
)
- color: Specifies the color of the progress indicator.
- strokeWidth: Adjusts the thickness of the circular progress indicator.
2. Determinate Progress Indicator
Determinate progress indicators are used when you know the progress of an operation, typically shown as a horizontal progress bar.
Example: Linear Determinate Progress Indicator
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun LinearDeterminateProgress(progress: Float) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
LinearProgressIndicator(
progress = progress, // Value between 0.0 and 1.0
modifier = Modifier.fillMaxWidth(),
color = MaterialTheme.colorScheme.secondary
)
Spacer(modifier = Modifier.height(16.dp))
Text("Progress: ${(progress * 100).toInt()}%")
}
}
@Preview(showBackground = true)
@Composable
fun PreviewLinearDeterminateProgress() {
var progress by remember { mutableStateOf(0.5f) }
LinearDeterminateProgress(progress)
}
Key Points:
LinearProgressIndicator
: This is used to display a horizontal progress bar.progress
: Theprogress
parameter accepts a value between 0.0 and 1.0, representing the percentage of progress (0.0 = 0%, 1.0 = 100%).- The
modifier
is used to set the width, andcolor
is used to define the progress bar’s appearance.
Customization of Linear Progress Indicator:
- color: You can change the color of the progress bar.
- modifier: You can adjust the size and padding of the progress indicator.
Handling Progress in Jetpack Compose
The progress value (for determinate indicators) is typically updated in response to some ongoing task, such as a network request, file upload, or computation. You can use state management techniques like remember
and mutableStateOf
to update and track the progress.
Example: Updating Progress in a Coroutine
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.*
@Composable
fun DownloadProgress() {
var progress by remember { mutableStateOf(0f) }
val scope = rememberCoroutineScope()
LaunchedEffect(progress) {
if (progress < 1f) {
scope.launch {
while (progress < 1f) {
delay(100) // Simulate download time
progress += 0.05f // Increase progress
}
}
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
LinearProgressIndicator(
progress = progress,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Text("Downloading: ${(progress * 100).toInt()}%")
}
}
@Preview(showBackground = true)
@Composable
fun PreviewDownloadProgress() {
DownloadProgress()
}
Key Points:
- LaunchedEffect: This composable is used to launch a coroutine and automatically cancel it when the state changes.
- delay(): Used to simulate a time-consuming operation (like a download).
- The
progress
value is updated periodically, and the UI reflects these changes with theLinearProgressIndicator
.
Combining Both Indicators
In some use cases, you might want to use both circular and linear progress indicators in the same screen. For instance, you could show a circular progress indicator while waiting for a task to start and then switch to a linear progress indicator when you know the task’s progress.
Example: Switching Between Circular and Linear Progress Indicators
@Composable
fun ProgressIndicatorSwitcher(isDownloading: Boolean, progress: Float) {
if (isDownloading) {
// Show circular progress
CircularProgressIndicator(
modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center)
)
} else {
// Show linear progress
LinearDeterminateProgress(progress)
}
}
This approach allows you to create a seamless user experience where the progress indicator adapts to the task’s state.
Conclusion
Progress indicators are an essential part of Android UI design, providing feedback to users about ongoing operations. Jetpack Compose simplifies the implementation of these indicators with easy-to-use CircularProgressIndicator
and LinearProgressIndicator
composables. Whether you’re dealing with indeterminate progress (like a network call) or determinate progress (like a file download), Jetpack Compose offers a declarative way to create beautiful, functional UI components.
By using remember
, mutableStateOf
, and LaunchedEffect
, you can dynamically update the progress values in your app, providing a smooth user experience. With the ability to customize the appearance and behavior of these progress indicators, Jetpack Compose empowers you to design rich, interactive, and user-friendly applications.