https://kotlinlang.org logo
Title
a

aidanvii

05/19/2023, 8:15 AM
I’m having this really strange issue with compose for desktop. With the following code, sometimes the Text doesn’t update:
import androidx.compose.material.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlin.time.Duration.Companion.seconds

fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        var counter: Int by remember { mutableStateOf(0) }
        LaunchedEffect(Unit) {
            while(isActive) {
                delay(1.seconds)
                counter++
            }
        }
        Text(
            text = remember(counter) {
                counter.toString().also {
                    println("counter: $it")
                }
            },
        )
    }
}
Here’s a video to demonstrate what I mean. It will occasionally not update.
org.jetbrains.compose
version is 1.4.0
j

Joel Denke

05/19/2023, 8:19 AM
Not sure if the issue, but why having remember on counter inside Text? Its already remembered earlier in same composable?
a

aidanvii

05/19/2023, 8:20 AM
I put the extra remember to print out
I’ll check without though
j

Joel Denke

05/19/2023, 8:22 AM
Could also try increment counter after its printed :)
a

aidanvii

05/19/2023, 8:24 AM
it still happens occasionally with this:
fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        var counter: Int by remember { mutableStateOf(0) }
        LaunchedEffect(Unit) {
            while (isActive) {
                delay(1.seconds)
                counter++
                println(counter)
            }
        }
        Text(
            text = counter.toString(),
        )
    }
}
I’ve checked with iOS and Android, this doesn’t happen 😕
j

Joel Denke

05/19/2023, 8:26 AM
And if doing println, counter++ and last delay in each loop? Feels like delay clock cycle different in desktop then causing compose skip frames as not fast enough, or cancels rendering as value changes before rendered
a

aidanvii

05/19/2023, 8:33 AM
println, counter++, delay has the same effect
j

Joel Denke

05/19/2023, 8:34 AM
Then not sure, I guess coroutines in desktop causing other context switching compared.
a

aidanvii

05/19/2023, 8:41 AM
Here is a slightly less trivial example that exhibits the same behaviour:
import androidx.compose.material.Text
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.isActive
import kotlin.coroutines.coroutineContext
import kotlin.time.Duration.Companion.seconds

fun main() {
    val counterViewModel = CounterViewModel()
    application {
        Window(onCloseRequest = ::exitApplication) {
            val counter: String by counterViewModel.counter.collectAsState()
            Text(text = counter)
        }
    }
}

class CounterViewModel {
    val counter: StateFlow<String> = flow {
        var counter = 0
        while (coroutineContext.isActive) {
            emit(counter.toString())
            delay(1.seconds)
            counter++
        }
    }.stateIn(
        scope = GlobalScope,
        started = SharingStarted.WhileSubscribed(
            stopTimeoutMillis = 0,
            replayExpirationMillis = 0
        ),
        initialValue = "",
    )
}
I think it’s something related to text. Here is a fairly peculiar example (don’t judge me, it’s horrible I know 🙈 )
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlin.time.Duration.Companion.seconds

fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        var counterAndColor: Pair<String, Color> by remember { mutableStateOf("0" to 0.toColor()) }
        LaunchedEffect(Unit) {
            while (isActive) {
                delay(1.seconds)
                counterAndColor = counterAndColor.run {
                    val next = (first.toInt() + 1)
                    next.toString() to next.toColor()
                }
                println(counterAndColor)
            }
        }
        Box(modifier = Modifier.background(color = counterAndColor.second)) {
            Text(text = counterAndColor.first)
        }
    }
}

fun Int.toColor(): Color = if (this % 2 == 0) Color.Green else Color.Red
a

Alexander Maryanovsky

05/19/2023, 10:44 AM
a

aidanvii

05/19/2023, 10:44 AM
Thank you 😄
a

Alexander Maryanovsky

05/19/2023, 10:46 AM
We’ll be releasing the next 1.4 update soon with the fix.
a

aidanvii

05/19/2023, 10:46 AM
Great to hear! I was losing my mind over this for a day 😂
a

Alexander Maryanovsky

05/19/2023, 10:47 AM
That’s nothing. I was losing my mind over it for a year.
a

aidanvii

05/19/2023, 10:54 AM
your effort is greatly appreciated 😅