I have a Compose Text that I want to update every ...
# codereview
t
I have a Compose Text that I want to update every second, because it shows a remaining time. Is the following the right/idiomatic way to express that?
Copy code
var remaining by remember { mutableStateOf(keyInfo.remaining) }
LaunchedEffect(remaining) {
   delay(1.seconds)
   remaining = keyInfo.remaining
}
SettingsLabel(text = remaining.clockPrinted())
e
there is no guarantee that the delay will resume or that the composition will be recomposed immediately - it is likely to drift
what you actually want to do is adjust the delay based on the current time, so that it's independent of any additional delays elsewhere, such as
Copy code
private fun Duration.truncatedTo(unit: Duration): Duration = unit.multipliedBy(this.dividedBy(unit))

@Composable
fun CountDownTimer(
    goal: Instant = Instant.ofEpochSecond(Int.MAX_VALUE.toLong()),
    tick: Duration = Duration.ofSeconds(1L),
    clock: Clock = Clock.systemUTC(),
) {
    val remaining by flow {
        do {
            val remaining = Duration.between(clock.instant(), goal)
            var next = remaining.truncatedTo(tick)
            if (next >= remaining) next -= tick
            next = next.coerceAtLeast(Duration.ZERO)
            delay((remaining - next).toMillis())
            emit(next)
        } while (next > Duration.ZERO)
    }.collectAsState(Duration.between(clock.instant(), goal).coerceAtLeast(Duration.ZERO).truncatedTo(tick))
    Text(text = remaining.toString())
}
(Android has https://developer.android.com/reference/android/os/CountDownTimer which also has similar functionality)
l
Sharing a bit on how I solved this particular issue
Mainly this bit:
Copy code
@IgnoredOnParcel
  val millisLeft = flow {
    while (true) {
      emit(calculateMillisLeft())
      delay(3)
    }
  }
The critical part is the
calculateMillisLeft
. As @ephemient pointed out, we want to avoid losing the small delays here and there.
e
having a wakeup every 3ms is absolute overkill
d
Honestly, I would deal with it in the ViewModel and submit the state in a flow
e
Sure, but wherever you implement it, you have to deal with delay skew
342 Views