How to handle focus in Jetpack Compose with examples
This guide will show you how to handle text focus in Jetpack Compose. There are different APIs responsible for different functionalities around text focus: the FocusRequester
, the FocusManager
and the various focus related Modifier
s. We'll cover all of them and how to utilize them in your Jetpack Compose applications:
How to request text focus
In order to request focus and make the soft keyboard to show, you need access to the FocusRequester
:
Column {
val focusRequester = remember { FocusRequester() }
var value by remember { mutableStateOf("") }
TextField(
modifier = Modifier.focusRequester(focusRequester),
value = value,
onValueChange = {
value = it
}
)
Button(onClick = {
focusRequester.requestFocus()
}) {
Text("Gain focus")
}
}
note how the FocusRequester
instance is passed to the focusRequester()
modifier of the TextField
and then is used when the Button
is clicked. The remember {}
is used so that we do not create a new FocusRequester
on every composition.
How to clear text focus
The FocusManager
is the class which exposes APIs regarding controlling focus on the screen. Its clearFocus()
function can be used to remove any focus like so:
val focusManager = LocalFocusManager.current
var value by remember { mutableStateOf("") }
TextField(
value = value,
onValueChange = {
value = it
}
)
LaunchedEffect(Unit) {
while (true) {
delay(2000)
focusManager.clearFocus()
}
}
in this example, the FocusManager
will clear any focus from the screen every 2 seconds.
How to pass focus to the next composable
The FocusManager
can only pass focus from one composable to the next. More specifically using the FocusManager.moveFocus()
function and passing the direction you need:
Column {
val focusManager = LocalFocusManager.current
var value by remember { mutableStateOf("") }
TextField(
value = value,
onValueChange = {
value = it
},
singleLine = true,
keyboardActions = KeyboardActions {
focusManager.moveFocus(FocusDirection.Next)
},
)
TextField(
value = value,
onValueChange = {
value = it
},
singleLine = true,
keyboardActions = KeyboardActions {
focusManager.clearFocus()
},
)
}
In the above example, pressing the soft keyboard's IME action while the first TextField
is pressed will move focus to the next TextField
. Doing the same while the second TextField
is focused, the focus will be cleared.
Passing a different FocusDirection
to the moveFocus()
function allows you to move towards any direction you need (such as backwards or up and down).
How to specify focus order
We saw how the FocusRequester
is responsible for capturing focus and how you need to pass an instance of it to the composable's Modifier
.
You can customize which composable's FocusRequester
is next or previous by override its focusProperties using the focusProperties()
modifier:
Row {
val focusManager = LocalFocusManager.current
Column {
val (a, b, c) = FocusRequester.createRefs()
TextField(
modifier = Modifier
.focusRequester(a)
.focusProperties {
next = b
},
value = "",
onValueChange = {},
)
TextField(
modifier = Modifier
.focusRequester(b)
.focusProperties {
previous = a
next = c
},
value = "",
onValueChange = {},
)
TextField(
modifier = Modifier
.focusRequester(c)
.focusProperties {
previous = b
},
value = "",
onValueChange = {},
)
}
Column {
Button(onClick = {
focusManager.moveFocus(FocusDirection.Previous)
}) {
Text("Previous")
}
Button(onClick = {
focusManager.moveFocus(FocusDirection.Next)
}) {
Text("Next")
}
}
}
The FocusRequester.createRefs()
is a convinience function which creates multiple FocusRequester
s instances which you can pass to multiple composables.
How to listen to focus changed events
There is a Modifier
for that. Use the onFocusChanged { }
Modifier. It provides a callback every time the focus state changes.
That was everything you need to know about focus in Jetpack Compose. We saw the three different parts of the focus API: the FocusRequester
, the FocusManager
and the related focus Modifier
s. Depending on your requirements you might want to use them in combination to achieve the result you need.