I'm facing a crash with my app when trying to requ...
# compose-desktop
a
I'm facing a crash with my app when trying to request the focus of a text field from a button onClick. This used to work on compose 1.3.0 but broke with compose 1.4.0 (even 1.5.0). Am I doing something wrong or it's an actual regression?
Copy code
fun main() = singleWindowApplication {
    var text by remember { mutableStateOf("") }
    val fieldFocusRequester = remember { FocusRequester() }

    OutlinedTextField(
        modifier = Modifier
            .focusRequester(fieldFocusRequester),
        value = text,
        onValueChange = {text = it },
        trailingIcon = {
            Button(
                onClick = { },
                modifier = Modifier.onFocusChanged { focusState ->
                        if (focusState.isFocused) {
                            // return focus to field instead so the user can continue typing without having to click on the field again
                            fieldFocusRequester.requestFocus()
                        }
                    },
            ) {
                Text("Click me")
            }
        }
    )
}
Stacktrace in the thread to avoid having an extremely long message
Copy code
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Check failed.
	at androidx.compose.ui.focus.FocusTransactionsKt.requestFocusForChild(FocusTransactions.kt:225)
	at androidx.compose.ui.focus.FocusTransactionsKt.requestFocus(FocusTransactions.kt:56)
	at androidx.compose.ui.focus.FocusRequester$requestFocus$1.invoke(FocusRequester.kt:66)
	at androidx.compose.ui.focus.FocusRequester$requestFocus$1.invoke(FocusRequester.kt:66)
	at androidx.compose.ui.focus.FocusRequester.findFocusTarget$ui(FocusRequester.kt:86)
	at androidx.compose.ui.focus.FocusRequester.requestFocus(FocusRequester.kt:66)
	at androidx.compose.foundation.ClickableKt$clickable$4$gesture$1$1$2.invoke-k-4lQ0M(Clickable.kt:179)
	at androidx.compose.foundation.ClickableKt$clickable$4$gesture$1$1$2.invoke(Clickable.kt:166)
	at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:258)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:178)
	at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
	at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
	at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:571)
	at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.dispatchPointerEvent(SuspendingPointerInputFilter.kt:459)
	at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:472)
	at androidx.compose.ui.node.BackwardsCompatNode.onPointerEvent-H0pRuoY(BackwardsCompatNode.kt:374)
	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:289)
	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:276)
	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:276)
	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:276)
	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:276)
	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:276)
	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:276)
	at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:156)
	at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:93)
	at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:98)
	at androidx.compose.ui.platform.SkiaBasedOwner.processPointerInput-gBdvCQM$ui(SkiaBasedOwner.skiko.kt:346)
	at androidx.compose.ui.platform.SkiaBasedOwner.processPointerInput-gBdvCQM$ui$default(SkiaBasedOwner.skiko.kt:339)
	at androidx.compose.ui.ComposeScene.processRelease(ComposeScene.skiko.kt:589)
	at androidx.compose.ui.ComposeScene.processPointerInput(ComposeScene.skiko.kt:565)
	at androidx.compose.ui.ComposeScene.access$processPointerInput(ComposeScene.skiko.kt:93)
	at androidx.compose.ui.ComposeScene$syntheticEventSender$1.invoke(ComposeScene.skiko.kt:221)
	at androidx.compose.ui.ComposeScene$syntheticEventSender$1.invoke(ComposeScene.skiko.kt:221)
	at androidx.compose.ui.SyntheticEventSender.sendInternal(SyntheticEventSender.kt:144)
	at androidx.compose.ui.SyntheticEventSender.send(SyntheticEventSender.kt:53)
	at androidx.compose.ui.ComposeScene.sendPointerEvent-WlEVilQ(ComposeScene.skiko.kt:535)
	at androidx.compose.ui.ComposeScene.sendPointerEvent-BGSDPeU(ComposeScene.skiko.kt:477)
	at androidx.compose.ui.ComposeScene.sendPointerEvent-BGSDPeU$default(ComposeScene.skiko.kt:460)
	at androidx.compose.ui.awt.ComposeLayer_desktopKt.onMouseEvent(ComposeLayer.desktop.kt:410)
	at androidx.compose.ui.awt.ComposeLayer_desktopKt.access$onMouseEvent(ComposeLayer.desktop.kt:1)
	at androidx.compose.ui.awt.ComposeLayer$onMouseEvent$1.invoke(ComposeLayer.desktop.kt:338)
	at androidx.compose.ui.awt.ComposeLayer$onMouseEvent$1.invoke(ComposeLayer.desktop.kt:331)
	at androidx.compose.ui.awt.ComposeLayer.catchExceptions(ComposeLayer.desktop.kt:81)
	at androidx.compose.ui.awt.ComposeLayer.onMouseEvent(ComposeLayer.desktop.kt:331)
	at androidx.compose.ui.awt.ComposeLayer.access$onMouseEvent(ComposeLayer.desktop.kt:54)
	at androidx.compose.ui.awt.ComposeLayer$4.mouseReleased(ComposeLayer.desktop.kt:312)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6626)
	at java.desktop/java.awt.Component.processEvent(Component.java:6391)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:746)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:744)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:743)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.awt.ComposeLayer$coroutineExceptionHandler$1@779a4125, androidx.compose.runtime.BroadcastFrameClock@457afcf8, StandaloneCoroutine{Cancelling}@537eafad, FlushCoroutineDispatcher@5f7b98d4]
Disconnected from the target VM, address: '127.0.0.1:58975', transport: 'socket'

Process finished with exit code 0
If the button is a standalone component instead of being inside the trailingIcon, it works as expected
Copy code
fun main() = singleWindowApplication {
    var text by remember { mutableStateOf("") }
    val fieldFocusRequester = remember { FocusRequester() }

    Column {
        OutlinedTextField(
            modifier = Modifier
                .focusRequester(fieldFocusRequester),
            value = text,
            onValueChange = { text = it },
        )

        Button(
            onClick = { },
            modifier = Modifier.onFocusChanged { focusState ->
                if (focusState.isFocused) {
                    // return focus to field instead so the user can continue typing without having to click on the field again
                    fieldFocusRequester.requestFocus()
                }
            },
        ) {
            Text("Click me")
        }
    }
}
m
Why do you requested for focus on focus gain rather on click?
a
Yeah, that's what I've changed and works, but wondering if the crash it's the expected behavior since it does not crash on android
There was another reason for doing it this way but I can't remember it right now... I should have added a comment 😅
m
Anyway it looks like a bug, might be something related how events are scheduled on Android compared to desktop 🤔 you should report it to issue tracker