Control focus and hardware keys
Move and clear focus
Keep each FocusRequester stable with remember, attach it to the destination, and declare
directional traversal on the source. Use LocalFocusManager.current for session-wide move or clear
operations. A requester must be attached to a mounted target before requestFocus() is called.
fun UiTreeBuilder.CredentialFocusForm() {
val email = rememberTextFieldState()
val password = rememberTextFieldState()
val passwordFocus = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
Column(
modifier = Modifier
.focusGroup()
.onPreviewKeyEvent { event ->
if (event.key == Key.Escape && event.type == KeyEventType.KeyDown) {
focusManager.clearFocus(force = true)
true
} else {
false
}
},
) {
TextField(
state = email,
label = "Email",
modifier = Modifier.focusProperties {
next = passwordFocus
down = passwordFocus
},
)
TextField(
state = password,
label = "Password",
modifier = Modifier.focusRequester(passwordFocus),
)
Button(
text = "Focus password",
onClick = { passwordFocus.requestFocus() },
)
}
}
onPreviewKeyEvent travels from the outermost declarative ancestor to the focused target. An
unconsumed event then bubbles from the target through onKeyEvent. Hardware keys are separate from
soft-keyboard composition, which continues through TextFieldState and the Android input bridge.
Restore a keyed child
Call saveFocusedChild() before temporarily removing or recycling a focus group, then call
restoreFocusedChild() after it returns. Restoration uses the declarative node key and remains
pending until a matching target mounts. Screen or process restoration still belongs to application
state followed by an explicit request after mounting.
Keep a pager editor visible
Vertical scroll owners reveal a focused editor through Android's native child-rectangle chain, even
when direct user scrolling is disabled. A pager owns whole-page selection, so place a potentially
obscured form in a page-local ScrollableColumn; the enclosing VerticalPager remains controlled
by application page state. There is no focus-follow flag. Control unwanted initial movement by
deciding which editor receives focus; do not disable the scroll owner's visibility contract. See
Modifier architecture for ownership, propagation, and Android
mapping invariants.