Issue
I would like to show a timer counting down in my composable, but I am not sure how to achieve this.
I was thinking to set a delay/timeout for a minute and trigger a recompose that way, but I am not sure if that's the right way to think about it.
@Composable
fun Countdown(completedAt: Date) {
val minutesLeft = ceil((completedAt.time - Date().time) / 60_000.0).toInt()
Handler(Looper.getMainLooper()).postDelayed({
// TODO: Recompose
}, 60_000)
Text(text = "$minutesLeft minutes until completed")
}
My goal is for the text to update every minute with the new time. How can I do this?
Solution
Store the amount of minutes as state.
Also make sure to clean up the postDelayed
callback inside a DisposableEffect
to prevent conflicting delays and memory leaks.
I have moved this logic to a minutesLeft
composable function so it can be reused.
@Composable
fun minutesLeft(until: Date): Int {
var value by remember { mutableStateOf(getMinutesLeft(until)) }
DisposableEffect(Unit) {
val handler = Handler(Looper.getMainLooper())
val runnable = {
value = getMinutesLeft(until)
}
handler.postDelayed(runnable, 60_000)
onDispose {
handler.removeCallbacks(runnable)
}
}
return value
}
private fun getMinutesLeft(until: Date): Int {
return ceil((until.time - Date().time) / 60_000.0).toInt()
}
Usage
@Composable
fun Countdown(completedAt: Date) {
val minutes = minutesLeft(until = completedAt)
Text(text = "$minutes minutes until completed")
}
Answered By - Duncan Lukkenaer
Answer Checked By - Clifford M. (JavaFixing Volunteer)