Home > Back-end >  How to refresh UI in Kotlin with Compose desktop when runBlocking?
How to refresh UI in Kotlin with Compose desktop when runBlocking?

Time:10-08

I'm learning Kotlin and Compose Desktop and I'm trying refresh the UI before fetch data from an API. But the request is running inside a runBlocking, thus the UI freezes until request is completed. This is my code, everything works.

val client = HttpClient(CIO)

@OptIn(ExperimentalComposeUiApi::class)
@Composable
@Preview
fun App() {
    var text by remember { mutableStateOf("Test button") }

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Column(
            modifier = Modifier.padding(50.dp)
        ) {
            Button(
                onClick = {

                    text = "Wait..."//How to refresh UI to display this text?
                    runBlocking {

                        delay(5000)//blocking test
                        val response: HttpResponse = client.request("https://myapi.com/") {
                            // Configure request parameters exposed by HttpRequestBuilder
                        }

                        if (response.status == HttpStatusCode.OK) {
                            val body = response.body<String>()
                            println(body)
                        } else {
                            println("Error has occurred")
                        }
                    }
                    text = "Test button"
                },
                modifier = Modifier.fillMaxWidth()
            ) {
                Text(text)
            }
            OutlinedTextField(
                value = "",
                singleLine = true,
                onValueChange = { text = it }
            )
        }
    }

}

fun main() = application {
    Window(
        onCloseRequest = ::exitApplication,
        state = WindowState(size = DpSize(350.dp, 500.dp)),
        title = "Compose test"
    ) {
        App()
    }
}

How to achieve that?

CodePudding user response:

The problem here is that you are using runBlocking at all.

The most straightforward solution for your case would be to replace your runBlocking {} with a coroutine scope. At the top of your App() function, create your scope: val scope = rememberCoroutineScope(), then instead of runBlocking you can say scope.launch {}.

New code would be:

@OptIn(ExperimentalComposeUiApi::class)
@Composable
@Preview
fun App() {
    var text by remember { mutableStateOf("Test button") }
val scope = rememberCoroutineScope()

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Column(
            modifier = Modifier.padding(50.dp)
        ) {
            Button(
                onClick = {

                    text = "Wait..."//How to refresh UI to display this text?
                    scope.launch {

                        delay(5000)//blocking test
                        val response: HttpResponse = client.request("https://myapi.com/") {
                            // Configure request parameters exposed by HttpRequestBuilder
                        }

                        if (response.status == HttpStatusCode.OK) {
                            val body = response.body<String>()
                            println(body)
                        } else {
                            println("Error has occurred")
                        }
                    }
                    text = "Test button"
                },
                modifier = Modifier.fillMaxWidth()
            ) {
                Text(text)
            }
            OutlinedTextField(
                value = "",
                singleLine = true,
                onValueChange = { text = it }
            )
        }
    }

}

I saw one comment say to use LaunchedEffect() but this won't work in your case since you can't use that in an onClick since it's not a Composable.

  • Related