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.
A LazyColumn is a vertically scrolling list that only composes and lays out the currently visible items. It's similar to a Recyclerview in the classic Android View system.
mutableStateOf creates an observable MutableState<T> , which is an observable type integrated with the compose runtime. interface MutableState<T> : State<T> { override var value: T. } Any changes to value will schedule recomposition of any composable functions that read value .
Jetpack Compose is designed to work with the established View-based UI approach. If you're building a new app, the best option might be to implement your entire UI with Compose. But if you're modifying an existing app, you might not want to fully migrate your app all at once.
Jetpack Compose is a modern toolkit designed to simplify UI development. It combines a reactive programming model with the conciseness and ease of use of the Kotlin programming language.
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) }
)
)
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... :(
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.
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()}
)
)
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
)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With