Remove keypad along with ModalBottomSheetLayout in Jetpack compose - android

After clicking outside BottomSheet, BottomSheet is hidden but the keypad remains persistent.
I want to remove the keypad as soon as BottomSheet is hidden in ModalBottomSheetLayout
After clicking the text in compose keypad pops up
After clicking outside bottom sheet, bottom sheet disappears but keypad persists
val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
ModalBottomSheetLayout(
sheetContent = {
Column {
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
label = { Text("Text") },
onValueChange = { text = it }
)
var text1 by remember { mutableStateOf("") }
OutlinedTextField(
value = text1,
label = { Text("Text1") },
onValueChange = { text1 = it }
)
OutlinedButton(
onClick = { click() },
) { Text(text = "SUBMIT") }
}
},
sheetState = state,
content = {
Greeting(state = state, scope = scope)
}
)

You need to add keyboard options and actions for the text fields. The keyboard options setup what type of keyboard and action you have on the keyboard. The onAction you pass the lambda to hide the bottom sheet and clear the focus:
val sheetState = rememberModalBottomSheetState(
skipHalfExpanded = true,
initialValue = ModalBottomSheetValue.Hidden
)
val focusRequester = remember { FocusRequester() }
OutlinedTextField(
value = text,
label = { Text("Text") },
onValueChange = { text = it },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
coroutineScope.launch {
focusRequester.freeFocus()
sheetState.hide()
}
}
)
)

Related

Show trailing icon after text input from user in TextField Jetpack Compose

I am trying to:
make the trailingIcon of TextField composable visible only if the user enters some text other than white spaces.
Later when the user clicks the trailingIcon the text in the TextField should get cleared and the trailingIcon should disappear.
Again when the user enters a text other than space, the trailingIcon should appear and enable this text clearing feature.
and so on...
I tried searching for solutions to this problem but mostly they were focused on "visible trailingIcons" and not what I was trying to implement.
Depending on text state you can specify null or actual view for trailingIcon parameter:
var text by remember { mutableStateOf("") }
val trailingIconView = #Composable {
IconButton(
onClick = {
text = ""
},
) {
Icon(
Icons.Default.Clear,
contentDescription = "",
tint = Color.Black
)
}
}
TextField(
value = text,
onValueChange = { text = it },
trailingIcon = if (text.isNotBlank()) trailingIconView else null,
)
You can add a condition to make visible the trailingIcon.
Something like:
var text by remember { mutableStateOf("") }
val isVisible by remember {
derivedStateOf {
text.isNotBlank()
}
}
OutlinedTextField(
value = text,
onValueChange = {
text = it
},
trailingIcon = {
if (isVisible) {
IconButton(
onClick = { text = "" }
) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = "Clear"
)
}
}
}
)

Enable and focus Textfield at once in Jetpack Compose

I'm trying to implement a TextField in Jetpack Compose with the following functionality: at first it is disabled, but when a user presses the Button, it gets enabled and at the same moment receives focus. This was my approach:
var text by remember { mutableStateOf("text") }
var enabled by remember { mutableStateOf(false)}
val focusRequester = remember { FocusRequester() }
Column {
TextField(
value = text,
onValueChange = { text = it },
enabled = enabled,
modifier = Modifier.focusRequester(focusRequester),
textStyle = TextStyle(fontSize = 24.sp)
)
Button(onClick = {
enabled = true
focusRequester.requestFocus()
}) {
Text("Enable and request focus")
}
But when the button is pressed, the TextField only gets enabled, not focused. To focus it, user has to click it once again. What am I doing wrong and what is the possible workaround?
You have to listen the change of the enabled parameter to give the focus to the TextField.
You can change your code to:
Button(onClick = {
enabled = true
}) {
Text("Enable and request focus")
}
LaunchedEffect(enabled) {
if (enabled){
focusRequester.requestFocus()
}
}
Simply add the delay after enabling textfield like this:
var text by remember { mutableStateOf("text") }
var enabled by remember { mutableStateOf(false)}
val focusRequester = remember { FocusRequester() }
val scope = rememberCoroutineScope()
Column {
TextField(
value = text,
onValueChange = { text = it },
enabled = enabled,
modifier = Modifier.focusRequester(focusRequester),
textStyle = TextStyle(fontSize = 24.sp)
)
Button(onClick = {
scope.launch {
enabled = true
delay(100)
focusRequester.requestFocus()
}
}) {
Text("Enable and request focus")
}
}
I once had a similar issue and I solved it by using interactionSource. The code looked something like this:
var text by remember { mutableStateOf("text") }
var enabled by remember { mutableStateOf(false)}
val focusRequester = remember { FocusRequester() }
val interactionSource = remember { MutableInteractionSource() } // add this
Column {
TextField(
value = text,
onValueChange = { text = it },
enabled = enabled,
modifier = Modifier
.focusable(interactionSource = interactionSource) // and this
.focusRequester(focusRequester),
textStyle = TextStyle(fontSize = 24.sp)
)
...
IIRC it was important to have .focusable() and .focusRequester() in the right order, but cannot remember which exactly, so try to swap them if it won't work immediately.

android:selectAllOnFocus in Jetpack Compose TextField

Traditional EditText on Android supports android:selectAllOnFocus attribute, which causes its content to be selected when user, for example, clicks on the EditText.
How can this behavior be achieved when using androidx.compose.material.TextField in Jetpack Compose?
You can collect the focus state from MutableInteractionSource and change the selection state depending on it:
var textFieldValue by remember { mutableStateOf(TextFieldValue("Lorem ipsum")) }
val interactionSource = remember { MutableInteractionSource() }
val isFocused by interactionSource.collectIsFocusedAsState()
LaunchedEffect(isFocused) {
textFieldValue = textFieldValue.copy(
selection = if (isFocused) {
TextRange(
start = 0,
end = textFieldValue.text.length
)
} else {
TextRange.Zero,
}
)
}
TextField(
value = textFieldValue,
onValueChange = { textFieldValue = it },
interactionSource = interactionSource,
)

Jetpack Compose Number Input in to TextField

I am currently unable to capture user input in to a textfield when the KeyboardType of the keyboard is set to KeyboardType.Number.
If the keyboard is set to KeyboardType.Text, the Textfield updates as expected, however when set to KeyboardType.Number, the Textfield fails to update.
Why is this? and how can I change my code so that when the Textfield is clicked, a Number Keyboard is displayed ,and, when numbers are pressed, the relevant numbers are updated in the Textfield.
The following code DOES NOT update the textfield (When set to KeyboardType.Number)...
#Composable
fun MyNumberField() {
var text = remember { mutableStateOf("")}
val change : (String) -> Unit = { it ->
value.value = it
}
TextField(
value = text.value,
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
onValueChange = change
)
}
The following code does update the textfield (When set to KeyboardType.Text)...
#Composable
fun MyNumberField() {
var text = remember { mutableStateOf("")}
val change : (String) -> Unit = { it ->
text.value = it
}
TextField(
value = value.value,
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
onValueChange = change
)
}
Many Thanks
You are supposed to update text.value, not value.value there is a typo in your code change it to this.
#Composable
fun MyNumberField() {
var text = remember { mutableStateOf("")}
val change : (String) -> Unit = { it ->
value.value = it // you have this which is not correct and I don't think it even compiled
text.value = it // it is supposed to be this
}
TextField(
value = text.value,
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
onValueChange = change
)
}

How to close the virtual keyboard from a Jetpack Compose TextField?

I'm using the Jetpack Compose TextField and I want to close the virtual keyboard when the user press the the action button (imeActionPerformed parameter).
val text = +state { "" }
TextField(
value = text.value,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done,
onImeActionPerformed = {
// TODO Close the virtual keyboard here <<<
}
onValueChange = { s -> text.value = s }
)
You can use the LocalSoftwareKeyboardController class to control the current software keyboard and then use the hide method:
var text by remember { mutableStateOf(TextFieldValue("Text")) }
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = text,
onValueChange = {
text = it
},
label = { Text("Label") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {keyboardController?.hide()})
)
This solution closes the keyboard without removing the focus from the current TextField.
Just to highlight the difference with:
val focusManager = LocalFocusManager.current
focusManager.clearFocus()
This code closes the keyboard removing the focus from the TextField.
Starting from compose 1.0.0-alpha12 (and still valid in compose 1.3.1) the onImeActionPerformed is deprecated and suggested approach is to use keyboardActions with combination of keyboardOptions:
val focusManager = LocalFocusManager.current
OutlinedTextField(
value = ...,
onValueChange = ...,
label = ...,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done, keyboardType = KeyboardType.Password),
)
focusManager.clearFocus() will take care of dismissing the soft keyboard.
In 1.0.0 you can either use SoftwareKeyboardController or FocusManager to do this.
This answer focuses on their differences.
Setup:
var text by remember { mutableStateOf("")}
TextField(
value = text,
onValueChange = { text = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { /* TODO */ }),
)
SoftwareKeyboardController:
Based on #Gabriele Mariottis answer.
val keyboardController = LocalSoftwareKeyboardController.current
// TODO =
keyboardController?.hide()
This only closes the keyboard, but does NOT clear the focus from any focused TextField (note the cursor & thick underline).
FocusManager:
Based on #azizbekians answer.
val focusManager = LocalFocusManager.current
// TODO =
focusManager.clearFocus()
This closes the keyboard AND clears the focus from the TextField.
Hiding the keyboard on button click
To add with Gabriele Mariotti's solution, if you want to hide the keyboard conditionally, say after a button click, use this:
keyboardController?.hide()
For example, hide the keyboard after clicking the Add button:
var newWord by remember { mutableStateOf("") }
val keyboardController = LocalSoftwareKeyboardController.current
// Setup the text field with keyboard as provided by Gabriele Mariotti
...
Button(
modifier = Modifier
.height(56.dp),
onClick = {
if (!newWord.trim().isNullOrEmpty()) {
wordViewModel.onAddWord(newWord.trim())
newWord = ""
keyboardController?.hide()
}
...
Edit after alpha-12 release:
See #azizbekian response.
Pre-alpha-12 response
I found the solution here :)
fun hideKeyboard(activity: Activity) {
val imm: InputMethodManager = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
var view = activity.currentFocus
if (view == null) {
view = View(activity)
}
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
I just need to call the function above from my component:
// getting the context
val context = +ambient(ContextAmbient)
// textfield state
val text = +state { "" }
TextField(
value = text.value,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done,
onImeActionPerformed = {
if (imeAction == ImeAction.Done) {
hideKeyboard(context as Activity)
}
}
onValueChange = { s -> text.value = s }
)
I found a way to shut him down in the CoreTextField,use TextInputService to control the switch
val focus = LocalTextInputService.current
var text by remember{ mutableStateOf("")}
TextField(
value = text,
onValueChange = { text = it },
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done, keyboardType = KeyboardType.Text),
keyboardActions = KeyboardActions(onDone = { focus?.hideSoftwareKeyboard() }),
singleLine = true
)
implementation 'androidx.compose.material3:material3:1.0.0-alpha02'
Text Field With Hide Keyboard On Ime Action
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun TextFieldWithHideKeyboardOnImeAction() {
val keyboardController = LocalSoftwareKeyboardController.current
var text by rememberSaveable { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Label") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = {
keyboardController?.hide()
// do something here
}
)
)
}

Categories

Resources