Jeff Lockhart
02/10/2024, 6:46 PMimmediate
version of the compose CoroutineDispatcher
in common code? Dispatchers.Main
throws an exception for desktop and it looks like the dispatcher used is actual FlushCoroutineDispatcher, which doesn't implement immediate
and is internal
.Jeff Lockhart
02/10/2024, 6:53 PMTextField
don't work well with asynchronous data sources. So an immediate
CoroutineDispatcher
is needed in order to make `StateFlow`s work.Arkadii Ivanov
02/10/2024, 6:58 PMJeff Lockhart
02/10/2024, 7:07 PMArkadii Ivanov
02/10/2024, 7:15 PMArkadii Ivanov
02/10/2024, 7:15 PMMitchell Syer
02/10/2024, 8:14 PMkv
02/11/2024, 2:42 AMJeff Lockhart
02/11/2024, 3:32 AMorg.jetbrains.kotlinx:kotlinx-coroutines-swing
dependency and found Compose still uses the internal FlushCoroutineDispatcher. I can't find an API to provide a different CoroutineDispatcher
to use.
The way I read those release notes:
Also, usage ofit seems that the dispatcher was later changed and is considered an implementation detail.orDispatchers.Swing
inside internals of Compose is implementation details, and can be changed in the future.Dispatchers.Main
Jeff Lockhart
02/11/2024, 3:37 AMCoroutineDispatcher
by default, so coroutines launched in the scope and flows collected would behave synchronously when possible. This is the case for viewModel and lifecycle scopes on Android.Jeff Lockhart
02/11/2024, 4:20 AMJeff Lockhart
02/11/2024, 4:57 AMDispatchers.Main.immediate
from the kotlinx-coroutines-swing
dependency works to synchronously launch a coroutine on the same thread as the Compose CoroutineScope
. Even though it's a different CoroutineDispatcher
implementation, they appear to use the same thread. I'm not sure if this is a guarantee Compose desktop provides or considered an implementation detail.Arkadii Ivanov
02/11/2024, 9:57 AMIt would be nice if the dispatcher used in the Compose scope was an immediateYes! I would also love to have this. But this looks unrelated to the issue of the missing or incorrect Main dispatcher in Compose for Desktop.
Arkadii Ivanov
02/11/2024, 9:58 AMArkadii Ivanov
02/11/2024, 10:01 AMJose Flavio
02/11/2024, 10:55 PMSystem.setProperty("kotlinx.coroutines.fast.service.loader", "false")
before your compose code
It seems to be a problem related to how the runtime is creating the dispatchers as soon as the app is launchedJose Flavio
02/11/2024, 10:57 PMAlexander Maryanovsky
02/12/2024, 10:37 AMTextField
and StateFlow
in TextField2
.Alexander Maryanovsky
02/12/2024, 10:42 AMrememberCoroutineScope
.Alexander Maryanovsky
02/12/2024, 10:42 AMfun main() = singleWindowApplication {
Column {
var textFieldValue by remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textFieldValue,
onValueChange = { textFieldValue = it }
)
val coroutineScope = rememberCoroutineScope(
getContext = { Dispatchers.Unconfined }
)
Button(
onClick = {
println("Before launch")
coroutineScope.launch {
println("In coroutine")
}
println("After launch")
}
) {
Text("Click me")
}
}
}
Arkadii Ivanov
02/12/2024, 10:42 AMAlexander Maryanovsky
02/12/2024, 10:43 AMArkadii Ivanov
02/12/2024, 10:45 AMAlexander Maryanovsky
02/12/2024, 10:51 AMDispatchers.Main.immediate
?Arkadii Ivanov
02/12/2024, 11:00 AMAlexander Maryanovsky
02/12/2024, 11:03 AMAlexander Maryanovsky
02/12/2024, 11:04 AMAlexander Maryanovsky
02/12/2024, 11:04 AMArkadii Ivanov
02/12/2024, 11:09 AMArkadii Ivanov
02/12/2024, 11:09 AMAlexander Maryanovsky
02/12/2024, 11:09 AMArkadii Ivanov
02/12/2024, 11:10 AMAlexander Maryanovsky
02/12/2024, 11:10 AMAlexander Maryanovsky
02/12/2024, 11:11 AMAlexander Maryanovsky
02/12/2024, 11:13 AMArkadii Ivanov
02/12/2024, 11:20 AMInput events are dispatched immediately as they occur. Recomposition only happens at most once every frame.Yeah, this makes sense. Seems like a trade-off.
Arkadii Ivanov
02/12/2024, 11:21 AMArkadii Ivanov
02/12/2024, 11:32 AMenabled: Boolean
to something like enabled: () -> Boolean
? I believe we have a similar API in HorizontalPager
, its PagerState
has pageCount: () -> Int
, perhaps for similar purposes.Alexander Maryanovsky
02/12/2024, 11:37 AMAs in comparison with normal Android Views, if I set ’isEnabled = false` to any view, then click listeners will stop working immediately.Ah, I misread that. I meant that it’s the same in Jetpack Compose on Android. Native Android views behave differently, of course.
Arkadii Ivanov
02/12/2024, 11:43 AMArkadii Ivanov
02/12/2024, 1:17 PMDispatchers.Main.immediate
but not with Dispatchers.Main
. It still looks pretty important to update the State synchronously, so that we can "see" the new state right after notifying the VM about the change.Jeff Lockhart
02/12/2024, 4:28 PMTextField2
addresses some of these issues specific to TextField
, but these sort of asynchronous state update issues can affect many component use cases, as Arkadii pointed out. And a workaround like his last button example is only possible if the state can be synchronously read as soon as it's updated, which Dispatchers.Main.immediate
provides for.
Is there a reason Compose doesn't use an immediate dispatcher by default like Android's ViewModel
and Lifecycle
`CoroutineScope`s?Alexander Maryanovsky
02/12/2024, 4:38 PMAlexander Maryanovsky
02/12/2024, 4:38 PMeygraber
02/12/2024, 6:54 PMimmediate
for a specific use case (but it's at the architecture level so it gets used everywhere). Would be nice to not have to do this (although my main use case for this is text updates, and that should go away with BTF2 as mentioned above)
https://github.com/eygraber/vice/blob/master/vice-portal/src/commonMain/kotlin/com/eygraber/vice/portal/VicePortal.kt#L32