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,
)
Related
I'm trying make BasicTextField select all contents when user taps on it.
This happens automatically if user will long press on the contents of BasicTextField but I want it happening without the long press.
There's a thread Select all text of TextField in Jetpack Compose but it utilizes TextFieldValue object which is hidden in BasicTextField but can be passed in TextField composable.
You can use the TextFieldValue also with a BasicTextField.
Instead of using the onFocusChanged modifier you can use the interactionSource and change the selection range depending on it.
Something like:
var textFieldValue by remember { mutableStateOf(TextFieldValue("Custom text")) }
val interactionSource = remember { MutableInteractionSource() }
val isFocused by interactionSource.collectIsFocusedAsState()
LaunchedEffect(isFocused) {
val endRange = if (isFocused) textFieldValue.text.length else 0
textFieldValue = textFieldValue.copy(
selection = TextRange(
start = 0,
end = endRange
)
)
}
BasicTextField(
value = textFieldValue,
onValueChange = { textFieldValue = it },
interactionSource = interactionSource
)
I'm trying to make a custom TextField where the text gets deleted when the right icon is pressed. The problem with the following code is that the textFieldValue.text is a val, so it can't be reassigned.
So far the only solution I have found is to recompose the entire TextField sending "" as the text. Is there a better way to achieve this?
var textFieldValue by rememberSaveable(stateSaver = TextFieldValue.Saver)
{ mutableStateOf(TextFieldValue("")) }
MyTextField(
text = textFieldValue,
onValueChange = {
textFieldValue = it
},
leftIconClickable = { /*Do nothing*/ },
rightIconClickable = { textFieldValue.text = "" }
)
(At this point this textField has almost the same code as the TextField of Jetpack Compose, the main difference being that it also receives 2 clickables for the icons)
Have your tried performing a copy()?
rightIconClickable = { textFieldValue = textFieldValue.copy(text = "") }
Here's a sample of a TextField using TextFieldValue with a simple Button where I copy() the current TextFieldValue instance, re-assigning it to the same variable, and it clears the TextField.
var textFieldValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(""))
}
Column {
TextField(
value = textFieldValue,
onValueChange = {
textFieldValue = it
}
)
Button(onClick = {
textFieldValue = textFieldValue.copy(text = "")}) {
}
}
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()
}
}
)
)
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.
I'd like to have a TextField bound to a MutableStateFlow that comes from a view model. This is how I set it up:
#Composable
fun MyTextField(textFlow: MutableStateFlow<String>) {
val state = textFlow.collectAsState(initial = "")
TextField(
value = TextFieldValue(state.value),
onValueChange = { textFlow.value = it.text },
label = { Text(text = "Label") }
)
}
When I type something into the text field, it behaves really strangely. For example, if I type 'asd', it ends up with 'asdasa'. How can I update textFlow.value without messing up with the text field?
This error is caused by the usage of TextFieldValue with Flow.
To fix this, set the value of the TextField to just state.value and then on text change set the value with textFlow.value = it.
#Composable
fun MyTextField(textFlow: MutableStateFlow<String>) {
val state = textFlow.collectAsState(initial = "")
TextField(
value = state.value,
onValueChange = { textFlow.value = it },
label = { Text(text = "Label") }
)
}