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.Swinginside 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