Progress indicators inform users about the status of ongoing processes, such as loading content, submitting forms, or saving updates. Lumo UI provides both linear and circular progress indicators in determinate and indeterminate variants.
Linear Progress Indicator
Usage
import com.nomanr.lumo.ui.components.progressindicators.LinearProgressIndicator
// Determinate (known progress)
LinearProgressIndicator(
progress = 0.7f // 70% complete
)
// Indeterminate (unknown progress)
LinearProgressIndicator()
Determinate Variant
Use when you can calculate the percentage of completion:
var progress by remember { mutableStateOf(0f) }
Column {
Text("Upload Progress: ${(progress * 100).toInt()}%")
LinearProgressIndicator(
progress = progress,
modifier = Modifier
.fillMaxWidth()
.height(8.dp)
)
Button(onClick = { progress += 0.1f }) {
Text("Increase")
}
}
Indeterminate Variant
Use when the progress duration is unknown:
LinearProgressIndicator(
modifier = Modifier.fillMaxWidth(),
color = AppTheme.colors.primary,
trackColor = AppTheme.colors.surface
)
Parameters
Progress value between 0.0 and 1.0 (determinate only)
modifier
Modifier
default:"Modifier"
Modifier for the indicator
color
Color
default:"AppTheme.colors.primary"
Color of the progress indicator
trackColor
Color
default:"AppTheme.colors.transparent"
Color of the track behind the indicator
strokeCap
StrokeCap
default:"StrokeCap.Round"
The stroke cap style for the indicator
Defaults
- Track Height: 4.dp
- Stroke Cap: StrokeCap.Round
- Animation Duration: 1800ms
Circular Progress Indicator
Usage
import com.nomanr.lumo.ui.components.progressindicators.CircularProgressIndicator
// Determinate (known progress)
CircularProgressIndicator(
progress = 0.75f // 75% complete
)
// Indeterminate (unknown progress)
CircularProgressIndicator()
Determinate Variant
Show specific progress as a circular arc:
var progress by remember { mutableStateOf(0f) }
Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator(
progress = progress,
modifier = Modifier.size(64.dp),
strokeWidth = 6.dp
)
Text(
text = "${(progress * 100).toInt()}%",
style = AppTheme.typography.body2
)
}
Indeterminate Variant
Use for loading states with unknown duration:
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
color = AppTheme.colors.primary
)
}
Parameters
Progress value between 0.0 and 1.0 (determinate only)
modifier
Modifier
default:"Modifier"
Modifier for the indicator
color
Color
default:"AppTheme.colors.primary"
Color of the progress indicator
trackColor
Color
default:"AppTheme.colors.transparent"
Color of the track behind the indicator
Width of the circular stroke
strokeCap
StrokeCap
default:"StrokeCap.Square"
The stroke cap style for the indicator
Defaults
- Diameter: 44.dp (Size: 48.dp - Stroke: 2.dp)
- Stroke Width: 4.dp
- Stroke Cap: StrokeCap.Square
- Rotations Per Cycle: 5
- Rotation Duration: 1332ms
Custom Styling
Custom Colors
// Linear with custom colors
LinearProgressIndicator(
progress = 0.6f,
color = Color.Green,
trackColor = Color.LightGray.copy(alpha = 0.3f)
)
// Circular with custom colors
CircularProgressIndicator(
progress = 0.6f,
color = Color.Blue,
trackColor = Color.Gray.copy(alpha = 0.2f)
)
Custom Sizes
// Thin linear indicator
LinearProgressIndicator(
progress = 0.5f,
modifier = Modifier
.fillMaxWidth()
.height(2.dp)
)
// Large circular indicator
CircularProgressIndicator(
progress = 0.5f,
modifier = Modifier.size(100.dp),
strokeWidth = 8.dp
)
Custom Stroke Cap
LinearProgressIndicator(
progress = 0.8f,
strokeCap = StrokeCap.Butt
)
CircularProgressIndicator(
progress = 0.8f,
strokeCap = StrokeCap.Round
)
Use Cases
Loading Content
if (isLoading) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
} else {
// Show content
}
File Upload
Column {
Text("Uploading file...")
LinearProgressIndicator(
progress = uploadProgress,
modifier = Modifier.fillMaxWidth()
)
Text("${(uploadProgress * 100).toInt()}% complete")
}
Pull to Refresh
Box(modifier = Modifier.pullToRefresh(
isRefreshing = isRefreshing,
onRefresh = { /* Refresh logic */ }
)) {
if (isRefreshing) {
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.TopCenter)
)
}
// Content
}
Best Practices
-
Choose the Right Type:
- Use linear for horizontal layouts or when showing progress inline
- Use circular for central loading states or compact spaces
-
Determinate vs Indeterminate:
- Use determinate when you can calculate progress percentage
- Use indeterminate when the operation duration is unknown
-
Visibility: Ensure indicators are clearly visible against backgrounds
-
Context: Provide context with labels like “Loading…”, “Uploading…”, etc.
-
Accessibility: Progress indicators automatically include semantic information
-
Performance: Indeterminate indicators use animations - avoid using many simultaneously
Animation Details
Linear Indeterminate
The linear indeterminate indicator uses a two-line animation:
- First Line: Animates from 0% to 100% with specific easing
- Second Line: Follows with offset timing
- Total Duration: 1800ms per cycle
Circular Indeterminate
The circular indeterminate indicator rotates while animating the arc:
- Base Rotation: 286° per cycle
- Arc Animation: Grows and shrinks from 0° to 290°
- Rotation Speed: 1332ms per rotation
- Cycles: 5 rotations per sequence
Source Reference
See the full implementation:
- Linear:
LinearProgressIndicator.kt:30-48 (determinate) and LinearProgressIndicator.kt:51-141 (indeterminate)
- Circular:
CircularProgressIndicator.kt:38-61 (determinate) and CircularProgressIndicator.kt:64-159 (indeterminate)