Dumitru Preguza
08/25/2025, 7:48 AMAlex Styl
08/25/2025, 8:08 AMTgo1014
08/25/2025, 8:13 AM/**
* ContentLoadingProgressBar implements a ProgressBar that waits a minimum time to be
* dismissed before showing. Once visible, the progress bar will be visible for
* a minimum amount of time to avoid "flashes" in the UI when an event could take
* a largely variable time to complete (from none, to a user perceivable amount).
*/
Dumitru Preguza
08/25/2025, 8:24 AMTgo1014
08/25/2025, 8:27 AMPavel Habžanský
08/25/2025, 8:34 AMAlex Styl
08/25/2025, 8:42 AMDumitru Preguza
08/25/2025, 8:47 AM@Composable
fun LoadingIndicator(
isLoading: Boolean,
modifier: Modifier = Modifier,
minDelay: Long = 300, // wait this long before showing
minShowTime: Long = 500 // once shown, stay visible at least this long
) {
var show by remember { mutableStateOf(false) }
var startTime by remember { mutableStateOf(0L) }
LaunchedEffect(isLoading) {
if (isLoading) {
// Wait before showing to prevent "blink"
delay(minDelay)
if (isLoading) { // still loading after delay
show = true
startTime = System.currentTimeMillis()
}
} else {
if (show) {
// Ensure minimum show time
val elapsed = System.currentTimeMillis() - startTime
if (elapsed < minShowTime) {
delay(minShowTime - elapsed)
}
show = false
}
}
}
if (show) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
}
Then use it:
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.collectAsState()
Box(modifier = Modifier.fillMaxSize()) {
// Your real content
Text("Data: ${uiState.data}")
// Debounced loading indicator
LoadingIndicator(isLoading = uiState.showLoading)
}
}
Pavel Habžanský
08/25/2025, 8:50 AMshowLoading
accordingly) so that UI state = UI but this should work too.Alex Styl
08/25/2025, 10:24 AMLoadingIndicator
can be 'not' loading. It's even simpler to add it to composition if your uisState.showLoading is true, instead of forwarding that to the componentDumitru Preguza
08/25/2025, 10:42 AMDumitru Preguza
08/25/2025, 10:44 AMPavel Habžanský
08/25/2025, 10:50 AM