Skip to main content
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
Float
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
Float
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
strokeWidth
Dp
default:"4.dp"
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

  1. Choose the Right Type:
    • Use linear for horizontal layouts or when showing progress inline
    • Use circular for central loading states or compact spaces
  2. Determinate vs Indeterminate:
    • Use determinate when you can calculate progress percentage
    • Use indeterminate when the operation duration is unknown
  3. Visibility: Ensure indicators are clearly visible against backgrounds
  4. Context: Provide context with labels like “Loading…”, “Uploading…”, etc.
  5. Accessibility: Progress indicators automatically include semantic information
  6. 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)

Build docs developers (and LLMs) love