I have a LazyColumn consisting of text fields with numeric input. When I switch between text fields, I sometimes see a flicker. It first opens the normal text keyboard and then switches to numeric one. This is leading to a really bad UX.
My code:
LazyColumn {
items(payers) {
Row {
Image(...)
Text(...)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.border(1.dp, Color.Gray, RoundedCornerShape(4.dp))
.padding(vertical = 4.dp)
) {
BasicTextField(
value = it.amount,
onValueChange = { /* Update it.amount */ },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
)
if (it.amount.isEmpty())
Text(
text = "₹ 0",
modifier = Modifier.alpha(0.5f)
)
}
}
}
}
Result:
How to avoid this flicker?
This looks like a known bug (specifically, that the keyboard type changes very quickly during focus request).
You can follow along on the bug tracker for updates.
Related
After setting up a TextField in the TopAppBar. I encountered two problems:
Textfiled gains focused after folding keyboard. When textfield is not triggered, it looks like pic 1. And when I press it(it is focused), the keyboard shows up and tesxfield looks like pic 2. If I fold the keyboard manually, the textfiled keeps pic 2 look. I wish it can turn back to pic 1 look.
Editting cursor (the pin shape stuff) remains on the screen when I switch to a new screen. pic3 shows the cursor. The color is the same as the background. I'll change it. When I switch to another screen, it remains there like pic 4 shows. It only lasts for a split second and disappears. But it is there.
For the first problem, I must missed some parameter I gusess.
I have no idea for the second one. Is it a compose bug?
#Composable
fun SearchBar() {
var text by remember { mutableStateOf("") }
Box(
modifier = Modifier
.height(70.dp)
.fillMaxWidth()
.background(MaterialTheme.colors.primary),
contentAlignment = Alignment.BottomCenter
) {
TextField(
leadingIcon = {
Icon(imageVector = Icons.Default.Search, contentDescription = null)
},
placeholder = {
Text(
"find more...",
color = Color.White
)
},
shape = RectangleShape,
colors = TextFieldDefaults.textFieldColors(
textColor = Color.White,
backgroundColor = MaterialTheme.colors.primary,
focusedIndicatorColor = Color.White,
unfocusedIndicatorColor = Color.Gray,
disabledIndicatorColor = Color.Black
),
value = text,
singleLine = true,
onValueChange = { text = it },
modifier = Modifier
.padding( bottom = 10.dp)
.size(360.dp,60.dp),
)
}
}
Ok thanks for the extra details, try this!
For part 1) this code can be added to the TextField to remove focus:
val focusManager = LocalFocusManager.current
TextField(
...
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {
keyboardController?.hide()
focusManager.clearFocus(force = true)
}),
)
Note the Done option will show a little check box in the keyboard you can use to close the keyboard and then this code will remove focus. I'm hoping onDone will also be called when manually closing the keyboard.
For part 2) I would reuse the approach. It sounds like a bug, but basically I would try calling LocalFocusManager.current.clearFocus(force = true) from where ever you are doing the navigation away from this screen, right before leaving, to force the thing to disappear.
Hope this helps!
Say I have a Row and I want it to intercept click events, preventing BasicTextField from gaining focus. Is it possible?
Row(
modifier = Modifier.clickable {}
) {
BasicTextField(...)
}
Using Enabled value in Textfield you can prevent BasicTextField from gaining focus.
TextField(
value = text,
onValueChange = { text = it},
enabled = false,
modifier = Modifier
.clickable { text= "Clicked"},
colors = TextFieldDefaults.textFieldColors(
disabledTextColor = LocalContentColor.current.copy(LocalContentAlpha.current),
disabledLabelColor = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium)
)
)
I have a TextField with a fixed height. When the user enters a longer text it will scroll. It will cut off any text within the padding when scrolling:
Basically something like this:
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { value -> text = value },
modifier = modifier
.fillMaxWidth()
.height(100.dp),
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
backgroundColor = Color.Transparent
)
)
It is possible to adjust/remove the padding for a TextField, by using BasicTextField directly, e.g. see this stack overflow question.
However I want to keep the padding, but without the clipping of the text when the user scrolls. A simple Text Composable has this behavior.
You can use BasicTextField and modify its decorationBox parameter.
Simply put innerTextField() inside a scrollable Column and add Spacers at the top and the bottom of it.
var text by remember {
mutableStateOf("Hello Stackoverflow")
}
val paddingSize = remember { 16.dp }
BasicTextField(
modifier = Modifier
.height(100.dp),
value = text,
onValueChange = { text = it },
decorationBox = { innerTextField ->
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(state = rememberScrollState())
) {
Spacer(modifier = Modifier.height(paddingSize))
innerTextField()
Spacer(modifier = Modifier.height(paddingSize))
}
}
)
Just use the maxLines parameter of the TextField to avoid clipping. Set it to 1 or 2, per your case, then adjust the height/font-size so the max lines you specify are accomodated correctly. It will likely start to snap to the visible portion, cutting the extra stuff entirely.
I want the placeholder not to disappear if I start typing in a field and it should be on the left side of the screen.
But the text input and cursor must be on the right.
An example is in the screenshot. Thanks!
Not the best solution, but it works.
Customizing the placeholder as per the requirement is not possible at this point in time. You have to create a completely customized TextField if that is an absolute requirement.
Note.
This is not a placeholder.
The positioning of the text is absolute, it should be adjusted according to the TextField size.
#Composable
fun FixedPlaceholder() {
var name by remember { mutableStateOf("") }
Box {
OutlinedTextField(
shape = MaterialTheme.shapes.medium,
value = name,
onValueChange = {
name = it
},
singleLine = true,
textStyle = LocalTextStyle.current.copy(
textAlign = TextAlign.End,
),
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = 16.dp,
vertical = 8.dp,
),
)
Text(
text = "to",
modifier = Modifier
.fillMaxWidth()
.padding(
start = 32.dp,
end = 32.dp,
top = 24.dp,
bottom = 8.dp
),
)
}
}
You don't need the placeholder, just set the leadingIcon property to:
leadingIcon={Text(text="To")}
In Compose, leadingIcon is a composable, and you can set it to anything you like.
I'm trying to make a textfield thats quite small (32.dp). When doing this, the label gets pushed to the bottom and I can't find a way to keep it centered? I've tried using TextAlign.center but no luck
TextField(
modifier = Modifier
.height(32.dp)
.padding(horizontal = 8.dp)
.border(1.dp, colorResource(id = R.color.mono4), RoundedCornerShape(32.dp))
.constrainAs(commentBox) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(profilePhoto.end)
end.linkTo(postText.start)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
},
label = {
Text(
textAlign = TextAlign.Center,
text = "Comment on workout...",
style = TextStyle(
fontSize = 14.sp,
color = colorResource(id = R.color.mono4),
fontFamily = FontFamily(Font(R.font.josefin_san_regular))
)
)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
value = commentText,
onValueChange = { commentText = it }
)
This is internal behaviour, you will need to alter the source coe to fix it, or implement your own composable, something like:-
#OptIn(ExperimentalAnimationApi::class)
#Composable
fun TinyTextCapture(){
var value by remember { mutableStateOf("") }
Box(contentAlignment = Alignment.Center){
TextField(
modifier = Modifier.height(32.dp),
value = value, onValueChange = { value = it }
)
AnimatedVisibility(visible = value.isEmpty()) {
Text("Label")
}
}
}
The default label parameter has a default padding from top start, that is why it was seen as being pushed downwards. If I were you, I would not go about editing the source of something as basic as a TextField since it has so many internal dependencies that it is all a mess.
You could also do without the AnimatedVisibility, and also apply a ConstraintLayout to get it at the start of the TextField in contrast to its absolute centered position right now.