When initially clicking the text field the whole view correctly shifts upwards so the keyboard is just below text entry.
After pressing return on the keyboard nothing on the screen moves and it leaves the cursor under the keyboard out of vision.
I had some other TextField issues that were solve by using the accompanist library. There is currently an assigned issue for it here. Here is the composable for the textField:
#Composable
fun EditTextContent(
label: String,
text: String,
isError: Boolean,
modifier: Modifier = Modifier,
singleLine: Boolean,
inputType: KeyboardType = KeyboardType.Text,
onTextChanged: (String) -> Unit
) {
val focusManager = LocalFocusManager.current
OutlinedTextField(
modifier = modifier.navigationBarsWithImePadding().testTag(label),
value = text,
onValueChange = {
onTextChanged(it)
},
label = { Text(label) },
singleLine = singleLine,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
keyboardOptions = KeyboardOptions(keyboardType = inputType),
isError = isError,
trailingIcon = {
if (isError) {
Icon(
painter = painterResource(id = R.drawable.ic_warning),
modifier = Modifier.size(25.dp),
contentDescription = null
)
} else {
null
}
},
)
}
Activity in the manifest is set to android:windowSoftInputMode="adjustPan" since adjustResize does nothing for some reason. Any help would be appreciated.
Related
.
I want my code to remove elements from list of text fields properly.
Each element has an X button to remove it's text field.
If I start removing elements from the bottom it works but it doesn't work for removing random elements
I want to use forEachIndexed for displaing the list
Please help me with solving this problem. I've been trying to do this for some time but every trial is unsuccessful.
This is a piece of code that I've managed to write but removing elements doesn't work properly
val listOfWords = mutableStateListOf<String>()
#Composable
fun Main() {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Words",
modifier = Modifier.padding(0.dp, 0.dp, 0.dp, 4.dp),
style = MaterialTheme.typography.h6
)
listOfWords.forEachIndexed { index, word ->
Input(word, 30, "Word", 1,
{newWord ->
listOfWords[index] = newWord
Log.d("text ",word)
},
{
listOfWords.removeAt(index)
}
)
}
IconButton(
onClick = {
listOfWords.add("")
}
) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Add"
)
}
}
}
#Composable
fun Input(
word: String,
maxChar: Int,
label: String,
maxLines: Int,
onEdit: (word: String) -> (Unit),
onRemove: () -> (Unit)
) {
var text by remember { mutableStateOf(word) }
Column(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp, 0.dp, 8.dp, 0.dp)
) {
OutlinedTextField(
value = text,
onValueChange = {
if (it.length <= maxChar) text = it
onEdit(text)
},
modifier = Modifier.fillMaxWidth(),
label = { Text(label) },
leadingIcon = {
Icon(Icons.Default.Edit, null)
},
trailingIcon = {
IconButton(onClick = {
onRemove()
}) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = "Back"
)
}
},
maxLines = maxLines
)
Text(
text = "${text.length} / $maxChar",
textAlign = TextAlign.End,
style = MaterialTheme.typography.caption,
modifier = Modifier
.fillMaxWidth()
.padding(end = 16.dp)
)
}
}
The problem is here.
var text by remember { mutableStateOf(word) }
Without supplying a key to Input's remember, compose will not be able refresh/update your remaining Input's states during Main's re-composition every time an Input is removed.
You can use the word parameter as key for remember to re-calculate every composition pass (i.e when you add/remove or typed a value in the TextField), and your code should probably work as you expected.
var text by remember(word) { mutableStateOf(word) }
Have you tried doing the following instead?
listOfWords.forEachIndexed { index, word ->
... // rest of code
{
listOfWords.removeAt(index)
}
I have a lazy column that contains a number of Text(string = "...") and a textfield pinned to the bottom of the screen below the LazyColumn. when the textfield is focused, the keyboard shows up and it pushes the textfield above the keyboard which is expected. however, when the items in the LazyColumn r too much, the textfield goes below the keyboard instead of allowing the user to scroll the lazy column. any reason for this? I already have set
android:windowSoftInputMode="adjustResize" in my manifest's activity. here's the full code:
Column(modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.SpaceBetween) {
LazyColumn {
items(viewModel.options) { option ->
Text(option.message)
}
}
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
modifier = Modifier.weight(1f),
value = optionsText,
onValueChange = {optionsText = it },
singleLine = true
)
Icon(
Icons.Filled.Send,
contentDescription = "",
modifier = Modifier.align(Alignment.CenterVertically).clickable {...}
)
}
}
Solution, WEIGHTS 🤪 !
LazyColumn(Modifier.weight(9f)) {
items(listItems) {
Text(it)
}
}
Row(modifier = Modifier.fillMaxWidth().weight(1f, /*false*/)) {
var loremText by remember { mutableStateOf("") }
TextField(
modifier = Modifier.weight(1f),
value = loremText,
onValueChange = { loremText = it },
singleLine = true
)
Icon(
Icons.Filled.Send,
contentDescription = "",
modifier = Modifier
.align(Alignment.CenterVertically)
.clickable { }
)
}
You may remove the adjustResize from your manifest.
I want to create TextField with exact 3 lines:
I want to see 3 lines even without any text in this TextField, i.e. I need a direct equivalent of EditText.lines in classic xml layout.
My not working code is:
OutlinedTextField(
value = currentText,
onValueChange = { currentText = it },
label = { Text ("Label") },
maxLines = 3,
modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(16.dp),
singleLine = false
)
Could you help me?
There is a feature request for this feature, I suggest your star it and maybe comment on it since it hasn't been updated for a while.
Until then you can use this hack. I render an invisible text field with extra lines so that it occupies the right size, and then apply that size to the real text field. I also pass modifier and textStyle as keys for remember for heightUpdateNeeded so that if you change them, the height will be recalculated. If any other parameters you pass may change the size of the view, you should pass them to remember as well.
#Composable
fun MinLinesOutlinedTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: #Composable (() -> Unit)? = null,
placeholder: #Composable (() -> Unit)? = null,
leadingIcon: #Composable (() -> Unit)? = null,
trailingIcon: #Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
minLines: Int,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
) {
val heightState = remember { mutableStateOf<Int?>(null) }
var heightUpdateNeeded by remember(modifier, textStyle) { mutableStateOf(true) }
val height = with(LocalDensity.current) {
heightState.value?.toDp()
} // to use if nullable unwrapping
Box(modifier.height(IntrinsicSize.Min).width(IntrinsicSize.Min)) {
if (heightUpdateNeeded) {
OutlinedTextField(
value = value + "\n".repeat(minLines),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
label = label,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
isError = isError,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = singleLine,
maxLines = maxLines,
interactionSource = interactionSource,
shape = shape,
colors = colors,
modifier = Modifier
.fillMaxSize()
.alpha(0f)
.onSizeChanged {
heightUpdateNeeded = false
println("onSizeChanged $it")
heightState.value = it.height
}
)
}
if (height != null) {
OutlinedTextField(
value = value,
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
label = label,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
isError = isError,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = singleLine,
maxLines = maxLines,
interactionSource = interactionSource,
shape = shape,
colors = colors,
modifier = Modifier
.fillMaxWidth()
.height(height)
)
}
}
}
We can show multiline edittext by setting height of OutlinedTextField. Following example is tested.
OutlinedTextField(
modifier = Modifier
.fillMaxWidth().height(120.dp)
.padding(start = 15.dp, top = 10.dp, end = 15.dp)
.background(Color.White, RoundedCornerShape(5.dp)),
shape = RoundedCornerShape(5.dp),
value = text,
onValueChange = { text = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
maxLines = 3,
textStyle = MaterialTheme.typography.caption
)
Attached image below.
Starting from Starting from M2 1.4.0-alpha02 and M3 1.1.0-alpha02 you can use the minLines attribute in the TextField and OutlinedTextField:
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text ("Label") },
minLines = 3,
maxLines = 3,
modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(16.dp)
)
It can be used with M2 and M3.
I had the same problem, and I found the solution as of now, basically, google has already solved the issue but it is in the Alpha release (androidx.compose.foundation:foundation:1.4.0-alpha01).
And to solve the issue I just copy paste HeightInLinesModifier.kt to our project and it is working fine by using Modifier.heightInLines. And as soon as it will become available in stable release We just need to delete this File from our project and change the import and we are good to go
I have a dialog with and an editable textfield. Everything is working fine but I cant't change cursor positon when I have already started editing the field. I have to press the back button and focus it again in correct spot. Any ideas?
#Composable
inline fun DialogInputText(display: MutableState<Boolean>, text: String, title: String, noinline onTextSet: (String)->Unit){
BaseDialog(display = display ) {
var text by remember { mutableStateOf(TextFieldValue(text)) }
DialogHeader(title = title)
EditText(text, { text = it}, modifier = Modifier.padding(8.dp).height(30.dp))
DialogButtons {
TextButton(onClick = {display.value = false} ) {
Text(text = "ZPÄšT")
}
TextButton(onClick = {onTextSet.invoke(text.text)}) {
Text(text = "POKRAÄŒOVAT")
}
}
}
}
#Composable
fun EditText(
text: TextFieldValue,
onValueChange: (TextFieldValue)->Unit,
validate: (TextFieldValue)->Boolean = {true},
modifier: Modifier = Modifier,
label: String = "",
textStyle:TextStyle = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Bold
),
keyboardType: KeyboardType = KeyboardType.Text,
color: Color = Colors.ChipGray
){
val valid = validate(text)
val color = if (valid) color else Colors.NotCompleted
Stack(modifier.background(color, shape = RoundedCornerShape(50)), alignment = Alignment.Center) {
if (text.text.isEmpty())
Text(text = label)
BaseTextField(
value = text,
onValueChange = onValueChange,
textStyle = textStyle,
modifier = Modifier.padding(start = 8.dp, end = 8.dp).fillMaxWidth(),
keyboardType = keyboardType,
)
}
}
It was discussed here. But I couldn't get working solution out of it.
Change Cursor Position and force SingleLine of TextField in Jetpack Compose
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
}
)
)
}