Jetpack compose: Setting ImeAction does not close or change focus for the keyboard - android

I'm using Jetpack compose 1.0.0-alpha07. I made a login screen that contains two TextFields customized using other composables.
However, setting ImeAction in keyboardOptions does not seem to work. For instance ImeAction.Next does not move focus to the next TextField. I think I should do something to make it possible, but no document or article has talked even briefly about ImeOptions. Here's the code I have for the screen:
Login composable:
EmailEdit(onChange = { email.value = it })
PasswordEdit(onChange = { password.value = it })
EmailEdit:
#Composable
fun EmailEdit(onChange: (String) -> Unit) {
val t = remember { mutableStateOf("") }
TextField(
value = t.value,
onValueChange = { value ->
t.value = value
onChange(value)
},
leadingIcon = { Icon(asset = Icons.Default.Email) },
label = { Text(text = "Email") },
maxLines = 1,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next, // ** Go to next **
keyboardType = KeyboardType.Email
),
visualTransformation = VisualTransformation.None
)
} errorHint = "Not a valid email"
)
}
PassEdit:
#Composable
fun PasswordEdit(onChange: (String) -> Unit) {
val t = remember { mutableStateOf("") }
TextField(
value = t.value,
onValueChange = { value ->
t.value = value
onChange(value)
},
leadingIcon = { Icon(asset = Icons.Default.Security) },
label = { Text(text = "Password") },
maxLines = 1,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done, // ** Done. Close the keyboard **
keyboardType = KeyboardType.Text
),
visualTransformation = PasswordVisualTransformation()
)
}
To perform Done and Next what code should I add?

You can use
keyboardOptions: software keyboard options that contains configuration such as KeyboardType and ImeAction
keyboardActions when the input service emits an IME action, the corresponding callback is called
For Done:
You can use the LocalSoftwareKeyboardController to interact with the keyboard.
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {keyboardController?.hide()}
)
For Next:
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = { focusRequester.requestFocus() }
)
Something like:
val (focusRequester) = FocusRequester.createRefs()
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = text,
onValueChange = {
text = it
},
label = { Text("Label") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = { focusRequester.requestFocus() }
)
)
TextField(
value = text,
onValueChange = {
text = it
},
modifier = Modifier.focusRequester(focusRequester),
label = { Text("Label") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { keyboardController?.hide() }
)
)

You can use LocalFocusManager.
val localFocusManager = LocalFocusManager.current
Inside parent composable of your fields.
To move focus to next field:
localFocusManager.moveFocus(FocusDirection.Down)
Inside onNext of KeyboardActions to move focus in specific direction such as left, right, up and down.
To clear focus:
localFocusManager.clearFocus()
Inside onDone of KeyboardActions to clear focus.
Email Field :
OutlinedTextField(
value = userId,
onValueChange = { userId = it },
label = { Text("Email") },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_account),
contentDescription = "ID"
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Color.Gray,
unfocusedBorderColor = Color.LightGray,
focusedLabelColor = Color(0xffcc0000)
),
keyboardOptions =
KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(onNext = {
localFocusManager.moveFocus(FocusDirection.Down)
})
)
Password Field :
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_password),
contentDescription = "Password"
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Color.Gray,
unfocusedBorderColor = Color.LightGray,
focusedLabelColor = Color(0xffcc0000)
),
keyboardOptions =
KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
localFocusManager.clearFocus()
})
)
Tried with version 1.0.1

use the onImeActionPerformed parameter.
For Done:
TextField(
onImeActionPerformed = { _, controller ->
controller?.hideSoftwareKeyboard()
}
)
For Next:
val focusRequester = remember { FocusRequester() }
TextField(
onImeActionPerformed = { _, _ ->
focusRequester.requestFocus()
}
)
TextField(
modifier = Mofifier.focusRequester(focusRequester)
)
Here's a working example:
val focusRequester = remember { FocusRequester() }
val email = remember { mutableStateOf("") }
TextField(
value = email.value,
onValueChange = { email.value = it },
imeAction = ImeAction.Next,
onImeActionPerformed = { _, _ -> focusRequester.requestFocus() }
)
val password = remember { mutableStateOf("") }
TextField(
value = password.value,
onValueChange = { password.value = it },
imeAction = ImeAction.Done,
onImeActionPerformed = { _, controller -> controller?.hideSoftwareKeyboard() },
modifier = Modifier.focusRequester(focusRequester)
)
Documentation:
FocusRequestModifier: https://developer.android.com/reference/kotlin/androidx/compose/ui/FocusRequesterModifier
TextField: https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#TextField(kotlin.String,%20kotlin.Function1,%20androidx.compose.ui.Modifier,%20androidx.compose.ui.text.TextStyle,%20kotlin.Function0,%20kotlin.Function0,%20kotlin.Function0,%20kotlin.Function0,%20kotlin.Boolean,%20androidx.compose.ui.text.input.VisualTransformation,%20androidx.compose.foundation.text.KeyboardOptions,%20kotlin.Int,%20kotlin.Function2,%20kotlin.Function1,%20androidx.compose.foundation.InteractionState,%20androidx.compose.ui.graphics.Color,%20androidx.compose.ui.graphics.Color,%20androidx.compose.ui.graphics.Color,%20androidx.compose.ui.graphics.Color,%20androidx.compose.ui.graphics.Shape)

With Compose in this instance you need to create a set of references to each focusable text field on your screen as below (textField1, textField2) and get a reference to the keyboardController.
Then you can add a type of action to your keyboard options signalling how the action button should look when the keyboard is displayed.
In the keyboardActions parameter, you can call a function on the specified IME action - in this case i've stated I want textField2 to take focus when the keyboard action button is pressed. You assign the references in the Modifier.focusRequester parameter in the TextField.
Lastly, to make your first TextField take focus when the screen appears, you call a DisposableEffect function where you specify you would like textField1 to take focus when the screen is first displayed.
val (textField1, textField2) = remember { FocusRequester.createRefs() }
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
modifier = Modifier.focusRequester(textField1),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions( onNext = {textField2.requestFocus()}
),
)
TextField(
modifier = Modifier.focusRequester(textField2),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions( onDone = {
keyboardController?.hide()}
),
)
DisposableEffect(Unit) {
textField1.requestFocus()
onDispose { }
}

Related

how to remove underline of outlinedtextfield compose when its focused?

When i try to enter some inputs to text field, it draws underline, how to remove this underline without change any color of outlinedtextfield.
OutlinedTextField(modifier = Modifier
.fillMaxWidth()
.padding(16.dp),value = password,
onValueChange = {
password = it},
leadingIcon = { Icon(painter = painterResource(id = R.drawable.ic_baseline_vpn_key_24), contentDescription = "icon-content")},
trailingIcon = { IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
Icon(painter = icon, contentDescription = "show-password")
}},
placeholder = { Text(text = "Password",
color = Color.LightGray)},
label = { BasicText(text = "Password")},
visualTransformation = if(passwordVisibility) VisualTransformation.None else PasswordVisualTransformation()
)
You should specify the keyboardType as Password to get rid of the text underline. You can do that by using KeyboardOptions:
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
value = password,
onValueChange = { password = it },
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_vpn_key_24),
contentDescription = "icon-content"
)
},
trailingIcon = {
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
Icon(painter = icon, contentDescription = "show-password")
}
},
placeholder = {
Text(
text = "Password",
color = Color.LightGray
)
},
label = { BasicText(text = "Password") },
visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password // HERE
),
)
This is the crucial change:
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password
)
The underline text is not related to color of OutlinedTextField.
It is related to the keyboardType applied. The default value is KeyboardType.Text.
Just add in your in OutlinedTextField:
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)

Android: enter or tab keys still enters values into the password input field using jetpack compose

When I enter or tab keys still enters values into the password input field using jetpack compose.
Below is my code snippet:
val (focusRequester) = FocusRequester.createRefs()
TextField(
value = text,
onValueChange = {
text = it
},
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { focusRequester.requestFocus() }
),
modifier = Modifier.onKeyEvent {
if (it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_ENTER){
focusRequester.requestFocus()
true
}
false
}
)
Tab key and enter key entries from laptop keyboard via Vysor not from android keyboard.
Any suggestions are welcome here. Thanks!
Update 1:
Is there any way we can do for all the function keys like standards, with out using below code.
modifier = Modifier.onKeyEvent {
if (it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_ENTER || it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_TAB) {
focusManager.moveFocus(FocusDirection.Next)
}
false
}
You can use a regex, something like:
val pattern = remember { Regex("^[^\\t\\n]*\$") }
val focusManager = LocalFocusManager.current
TextField(
value = text,
onValueChange = {
if (it.isEmpty() || it.matches(pattern)) {
text = it
}
},
singleLine = true,
maxLines= 1,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { focusManager.moveFocus(FocusDirection.Next) }
),
modifier = Modifier.onKeyEvent {
if (it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_ENTER || it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_TAB) {
focusManager.moveFocus(FocusDirection.Next)
}
false
}
)
I think it should help you to clear focus after clicking on keyboard Done button.
val focusManager = LocalFocusManager.current
TextField(
value = text,
onValueChange = {
text = it
},
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { focusManager.clearFocus() }
),
)
val (focusRequester) = FocusRequester.createRefs()
TextField(
value = text,
onValueChange = {
if (it.isEmpty() || it.matches(pattern)) {
text = it
}
},
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { focusRequester.requestFocus() }
),
modifier = Modifier.onKeyEvent {
if (it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_ENTER || it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_TAB) {
focusManager.moveFocus(FocusDirection.Next)
}
false
}
)

Handle back button click when keyboard is open in Jetpack Compose

The below-provided code is a simplified version of my problem.
I want to trigger an action when the device back button is clicked when the keyboard is open.
It is possible to handle the back button click using BackHandler when the keyboard is closed. But I want it even if the keyboard is open.
#Composable
fun BackHandlingWhenKeyboardOpen() {
val focusManager = LocalFocusManager.current
BackHandler(
enabled = true,
) {
// This is not triggered when keyboard is open
Log.d("TEST_TAG", "Back Handler")
}
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
) {
focusManager.clearFocus()
}
) {
TextField(
value = "",
onValueChange = {},
keyboardActions = KeyboardActions(
onDone = {
focusManager.clearFocus()
},
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.NumberPassword,
imeAction = ImeAction.Done,
),
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
)
}
}

Is there a way to show "italic, "bold", "underline" buttons on Android keyboard?

I am working on an Android app in compose,
When writing text, I need to show a keaybord that has the "underline", "italic" and "bold" buttons present on top of the keyboard text.
Is there a way I can do that?
Currently, this is what I have:
val keyboardController = LocalSoftwareKeyboardController.current
var textValue by remember { mutableStateOf(TextFieldValue("Write your text...")) }
TextField(
value = textValue,
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
placeholder = { Text(text = "John Doe") },
textStyle = TextStyle(
color = Color.Gray,
fontSize = 20.sp,
// fontWeight = FontWeight.Bold,
// textDecoration = TextDecoration.Underline
),
// Update value of textValue with the latest value of the text field
onValueChange = {
textValue = it
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {
keyboardController?.hide()
// do something here
}
) )

Jetpack Compose TextField capture keyboard Enter-input

From what I understand, there is an API for the Jetpack Compose Textfield for capturing Keyboard actions but I don't know what of this APIs that can capture the Enter-Input
The use-case of this capturing enter input is to enable to click Enter and try to go to the next TextField and keeping while keeping the keyboard open
OutlinedTextField(
value = username.value,
onValueChange = {
username.value = it
},
keyboardActions = KeyboardActions(
onDone = {},
onGo = {},
onNext = {},
onPrevious ={},
onSearch ={},
onSend = {}
)
)
You can use something like:
val (focusRequester) = FocusRequester.createRefs()
TextField(
value = text,
onValueChange = {
text = it
},
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { focusRequester.requestFocus() }
),
modifier = Modifier.onKeyEvent {
if (it.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_ENTER){
focusRequester.requestFocus()
true
}
false
}
)
TextField(
value = text2,
onValueChange = {
text2 = it
},
modifier = Modifier.focusRequester(focusRequester),
)

Categories

Resources