'What is the simplest way to set the focus order in Jetpack Compose?
I have a column of TextFields, something like:
Column {
TextField(
value = ...,
onValueChange = { ... },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.next),
)
TextField(
value = ...,
onValueChange = { ... },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.next),
)
.
.
.
}
I would like to have the focus on each TextField move to the next when the user press Tab, or the next button on the keyboard. Currently pressing Tab inserts a tab into the TextField. Pressing the next button does nothing. I can create a FocusRequester for each TextField and set the keyboardActions onNext to request focus on the next field for each one. This is a little tedious and it doesn't address the Tab behavior.
Solution 1:[1]
I recently found this article: https://medium.com/google-developer-experts/focus-in-jetpack-compose-6584252257fe
It explains a different way to handle focus that is quite a bit simpler.
val focusManager = LocalFocusManager.current
TextField(
modifier = Modifier
.onPreviewKeyEvent {
if (it.key == Key.Tab && it.nativeKeyEvent.action == ACTION_DOWN){
focusManager.moveFocus(FocusDirection.Down)
true
} else {
false
}
},
value = text,
onValueChange = { it -> text = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }
)
)
Solution 2:[2]
Not sure if it's the easier way, but you can create a FocusRequester
object for each field and request the focus following the order that you want.
@Composable
fun FocusRequestScreen() {
// Create FocusRequesters... (you can use createRefs function)
val focusRequesters = List(3) { FocusRequester() }
Column {
TextFieldWithFocusRequesters(focusRequesters[0], focusRequesters[1])
TextFieldWithFocusRequesters(focusRequesters[1], focusRequesters[2])
TextFieldWithFocusRequesters(focusRequesters[2], focusRequesters[0])
}
}
@Composable
private fun TextFieldWithFocusRequesters(
focusRequester: FocusRequester,
nextFocusRequester: FocusRequester
) {
var state by rememberSaveable {
mutableStateOf("Focus Transition Test")
}
TextField(
value = state,
onValueChange = { text -> state = text },
// Here it is what you want...
modifier = Modifier
.focusOrder(focusRequester) {
nextFocusRequester.requestFocus()
}
,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
)
}
I get this code from here. It didn't solve the tab issue though... :(
Solution 3:[3]
I'm currently on compose_version = '1.0.2'
.
Focus is not moved when you press the next button because, although the default keyboard action is to move focus to the next one, compose seems doesn't know which one should be the next one. Creating FocusRequester
s for each item and set their focus order via Modifier.focusOrder() {}
could work (btw, no need to set the keyboardActions
's onNext
to request focus if you are choosing this way), but since your TextField
s are in the same Column
, you can just set keyboardActions
to tell compose move the focus to the one in the down direction. Something like:
Column {
val focusManager = LocalFocusManager.current
TextField(
value = "", onValueChange = {},
keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next ),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }
)
)
TextField(
value = "", onValueChange = {},
keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next ),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }
)
)
TextField(
value = "", onValueChange = {},
keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next ),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }
)
)
}
After you did this, the next button on the IME keyboard should works.
For the Tab key, since TextField
doesn't deal with Tab key automately, so you might want to use focusManager
inside Modifier.onKeyEvent{}
to move focus in the same way as the above example did.
Solution 4:[4]
About the order you can check the @nglauber answer.
To use the Tab key you can use the onKeyEvent
modifier.
TextField(
modifier = Modifier
.focusRequester(focusRequester)
.onKeyEvent {
if (it.key.keyCode == Key.Tab.keyCode){
focusRequesterNext.requestFocus()
true //true -> consumed
} else false },
value = text,
onValueChange = { it -> text = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = {focusRequesterNext.requestFocus()}
)
)
Solution 5:[5]
Set singleLine , please try
OutlinedTextField(
...
singleLine = true,
)
Simple example
@Composable
fun Test() {
val focusManager = LocalFocusManager.current
var text1 by remember {
mutableStateOf("")
}
var text2 by remember {
mutableStateOf("")
}
Column() {
OutlinedTextField(value = text1, onValueChange = {
text1 = it
},
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next,
keyboardType = KeyboardType.Text
),
keyboardActions = KeyboardActions(
onNext ={
focusManager.moveFocus(FocusDirection.Down)
}
),
singleLine = true
)
OutlinedTextField(value = text2, onValueChange = {
text2 = it
},
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next,
keyboardType = KeyboardType.Text
),
keyboardActions = KeyboardActions(
onNext ={
focusManager.moveFocus(FocusDirection.Down)
}
),
singleLine = true
)
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | kassim |
Solution 2 | nglauber |
Solution 3 | |
Solution 4 | |
Solution 5 |