Quick question here:
Is this a bug inside jetpack compose TextField or am I missing something:
#Composable
fun TextFieldWithPlaceholder() {
val text = rememberSaveable {
mutableStateOf("")
}
Box(modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background)) {
TextField(
modifier = Modifier
.background(Color.Blue)
.size(width = 300.dp, height = 50.dp),
value = text.value,
onValueChange = { value ->
text.value = value
},
singleLine = true,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
focusedLabelColor = Color.Transparent,
unfocusedLabelColor = Color.Transparent,
placeholderColor = Color.Red,
cursorColor = Color.White
),
placeholder = {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
text = "Placeholder"
)
},
textStyle = MaterialTheme.typography.body2.copy(textAlign = TextAlign.Center)
)
}
}
If you build the project with this composable, and tap inside the input field, you'll notice that the cursor is behind the placeholder text (talking about the Z axis). Parts of the cursor are not visible due to "Placeholder" text being on top of it.
Is there a way to force it to be on top of placeholder?
Thanks in advance
Related
I almost completely successfully implemented the custom search component I needed, but I have one last problem. I need my component to be only 45dp to match the designer's requirements, but at that height, due to the TextField padding, it's clipping the text on top. I leave you below a screenshot of what is happening and the code I have. Is there any way to resolve this?
I'm new to Jetpack Compose and that's all I need to close this component.
#Preview
#Composable
fun SearchTextField() {
val state = remember { mutableStateOf(TextFieldValue("")) }
TextField(
value = state.value,
onValueChange = { value -> state.value = value },
textStyle = TextStyle18Normal,
placeholder = {
Text(text = stringResource(id = R.string.search), style = TextStyle18Normal)
},
trailingIcon = {
Image(
painter = painterResource(id = R.drawable.ic_search),
contentDescription = ""
)
},
singleLine = true,
shape = RoundedCornerShape(Dimen13),
colors = TextFieldDefaults.textFieldColors(
cursorColor = Color.DarkGray,
trailingIconColor = colorResource(id = R.color.purple),
backgroundColor = Color.White,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
modifier = Modifier
.width(Dimen320)
.height(Dimen45)
)
}
If I remove the .height(45) my problem is solved, but the component is not correct.
The TextField has hardcoded paddings.
Starting with 1.2.0 you can use the BasicTextField together with the TextFieldDecorationBox. With the contentPadding attribute you can define custom vertical paddings.
Something like:
val colors = TextFieldDefaults.textFieldColors(backgroundColor = White)
BasicTextField(
value = value,
onValueChange = onValueChange,
textStyle = TextStyle.Default.copy(fontSize = 18.sp),
modifier = modifier
.background(
color = colors.backgroundColor(enabled).value,
shape = RoundedCornerShape(13.dp). //rounded corners
)
.indicatorLine(
enabled = enabled,
isError = false,
interactionSource = interactionSource,
colors = colors,
focusedIndicatorLineThickness = 0.dp, //to hide the indicator line
unfocusedIndicatorLineThickness = 0.dp //to hide the indicator line
)
.height(45.dp),
interactionSource = interactionSource,
enabled = enabled,
singleLine = singleLine
) {
TextFieldDefaults.TextFieldDecorationBox(
value = value,
innerTextField = it,
singleLine = singleLine,
enabled = enabled,
visualTransformation = VisualTransformation.None,
trailingIcon = { /* ... */ },
placeholder = { /* ... */ },
interactionSource = interactionSource,
// keep horizontal paddings but change the vertical
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 0.dp, bottom = 0.dp
)
)
}
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.
I have created a Text within a TextField to work as hint. I want to reduce the distance between the underbar and the hint text. How do I do that? I have tried using the following code:
TextField(
value = query.value,
onValueChange = { newValue -> query.value = newValue },
label = {
Text(
"Name as on Pan Card",
color = colorResource(id = R.color.fade_green),
modifier = Modifier.align(BottomCenter) //THIS LINE PRODUCES ERROR
)
},
textStyle = TextStyle(
textAlign = TextAlign.Start,
color = colorResource(id = R.color.fade_green),
fontFamily = FontFamily(Font(R.font.poppins_regular)),
fontSize = with(LocalDensity.current){
dimensionResource(id = R.dimen._11ssp).toSp()},
),
modifier = Modifier
.align(
CenterHorizontally
)
.weight(0.3f),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
unfocusedIndicatorColor = colorResource(id = R.color.bright_green),
focusedIndicatorColor = colorResource(id = R.color.bright_green)
)
)
I am getting the error 'fun Modifier.align(alignment : Alignment) : Modifier can't be called in this context by implicit receiver. Use the explicit one if necessary'
How do I solve this?
Instead of
modifier = Modifier.align(BottomCenter)
use
modifier = Modifier.align(Alignment.BottomCenter)
In general, most components in Jetpack Compose seem to be very easy to customize.
However, the same cannot be said for the TextField. For example, say that I wanted to make something like this:
One would think that simply wrapping the BaseTextField would work. However, it appears that there has been a bug in the BaseTextField component, and I have opened an issue. This bug will not permit the user to focus the text field after focusing-away from it once already, until the component is re-rendered.
Citing this, I attempted to customize the OutlinedTextField and TextField components, but am not able to customize them to look like the image above. Were it not for the fact that the cursor color uses the activeColor property, I could make it work.
What would be a proper work-around to create a usable text field that looks like the above?
You can use the TextField:
removing the label with label = null
applying custom color with the TextFieldDefaults.textFieldColors parameter to hide the indicator.
adding in the onValueChange a function to fix the max number of characters as described here
Finally use a Column to add 2 Text composables to complete the external label and counter text.
Something like:
var text by remember { mutableStateOf("") }
val maxChar = 5
Column(){
//External label
Text(
text = "Label",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Start,
color = Blue
)
TextField(
value = text,
onValueChange = {
if (it.length <= maxChar) text = it
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
shape = RoundedCornerShape(8.dp),
trailingIcon = {
Icon(Icons.Filled.Add, "", tint = Blue)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = ....,
focusedIndicatorColor = Color.Transparent, //hide the indicator
unfocusedIndicatorColor = .....)
)
//counter message
Text(
text = "${text.length} / $maxChar",
textAlign = TextAlign.End,
color = Blue,
style = MaterialTheme.typography.caption, //use the caption text style
modifier = Modifier.fillMaxWidth()
)
By this exemple you can learn a lot. With 1.0.0 you can do like this:
Column {
var textState by remember { mutableStateOf("") }
val maxLength = 110
val lightBlue = Color(0xffd8e6ff)
val blue = Color(0xff76a9ff)
Text(
text = "Caption",
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 4.dp),
textAlign = TextAlign.Start,
color = blue
)
TextField(
modifier = Modifier.fillMaxWidth(),
value = textState,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = lightBlue,
cursorColor = Color.Black,
disabledLabelColor = lightBlue,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
onValueChange = {
if (it.length <= maxLength) textState = it
},
shape = RoundedCornerShape(8.dp),
singleLine = true,
trailingIcon = {
if (textState.isNotEmpty()) {
IconButton(onClick = { textState = "" }) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = null
)
}
}
}
)
Text(
text = "${textState.length} / $maxLength",
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
textAlign = TextAlign.End,
color = blue
)
}
Well, until the issue I mentioned is resolved, the choices are:
Roll back to Compose version 1.0.0-alpha04 (issue was introduced in alpha05)
Add a border to a TextField or OutlinedTextField as so:
TextField(
value = myValue,
onValueChange = myOnChange,
modifier = Modifier.clip(myShape).border(5.dp, myColor)
)
In addition to Gabriele Mariotti's Answer
If you like to have custom color for your cursor, you can achieve it using:
Column(){
//External label
Text(
...
...
)
TextField(
...
...
colors = TextFieldDefaults.textFieldColors(
backgroundColor = ...,
focusedIndicatorColor = ...,
unfocusedIndicatorColor = ...,
cursorColor = Color.Black)
)
//counter message
Text(
...
...
)
I would like to remove the purple line/indicator (see the following image) of TextField.
Is that possible or should I create my own custom TextField to achieve that?
This has been changed in the recent Jetpack Compose UI Beta release 1.0.0-beta01 now you can pass the
TextFieldDefaults with desired colors.
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
backgroundColor = Color.LightGray,
)
example
TextField(
value = searchText,
onValueChange = { Log.d(HOME_COMPONENT, it) },
label = { Text(text = "Search") },
shape = RoundedCornerShape(10.dp),
leadingIcon = {
Image(
painter = painterResource(id = R.drawable.ic_search),
contentDescription = "search"
)
},
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
backgroundColor = Color.LightGray,
)
)
image:
or if you want to customize the component according to your UI/UX then use the BasicTextField
#Composable
fun ToolbarComponent() {
var searchText by remember { mutableStateOf("") }
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(id = R.drawable.ic_search),
contentDescription = "search",
modifier = Modifier.size(20.dp),
tint = iconTintColor
)
Spacer(modifier = Modifier.size(16.dp))
BasicTextField(
value = searchText,
onValueChange = { searchText = it },
modifier = Modifier
.background(shape = RoundedCornerShape(10.dp), color = Color.LightGray)
.fillMaxWidth()
.padding(16.dp),
decorationBox = {
Text(text = "Search")
}
)
}
}
Starting with 1.2.0-alpha04 you can use the TextFieldDecorationBox together with BasicTextField to build a custom text field based on Material Design text fields.
In your case you can apply the indicatorLine modifier to define the focusedIndicatorLineThickness and the unfocusedIndicatorLineThickness parameters:
var text by remember { mutableStateOf("") }
val singleLine = true
val enabled = true
val interactionSource = remember { MutableInteractionSource() }
BasicTextField(
value = text,
onValueChange = { text = it },
modifier = Modifier
.indicatorLine(enabled, false,
interactionSource,
TextFieldDefaults.textFieldColors(),
focusedIndicatorLineThickness = 0.dp,
unfocusedIndicatorLineThickness = 0.dp
)
.background(
TextFieldDefaults.textFieldColors().backgroundColor(enabled).value,
TextFieldDefaults.TextFieldShape
)
.width(TextFieldDefaults.MinWidth),
singleLine = singleLine,
interactionSource = interactionSource
) { innerTextField ->
TextFieldDecorationBox(
value = text,
innerTextField = innerTextField,
enabled = enabled,
singleLine = singleLine,
visualTransformation = VisualTransformation.None,
interactionSource = interactionSource,
label = { Text("Label") }
)
}
Otherwise you can use TextField defining these attributes:
focusedIndicatorColor
unfocusedIndicatorColor
disabledIndicatorColor
Something like:
TextField(
....
colors = TextFieldDefaults.textFieldColors(
backgroundColor = .....,
focusedIndicatorColor = Transparent,
unfocusedIndicatorColor = Transparent)
)
If You use TextField in that you can give the activeColor to Color.Transparent
Note: activeColor is not only for indicator, its for label bottom indicator and cursor
Ex:
var text: String by mutableStateOf("")
TextField(value = text, onValueChange = {
text = it
}, activeColor = Color.Transparent)
As per the document, activeColor is
activeColor the color of the label, bottom indicator and the cursor
when the text field is in focus
If you want to use your own you can try BaseTextField
Actually (version alpha 7) this is the easiest version I have found to remove Divider.
Set activeColor and inactiveColor to Color.Transparent in order to hide the indicator line under the TextField, whatever his state.
If you add only inactiveColor to Color.Transparent, the line will be invisible only when TextField is not focused
Add textStyle = TextStyle(color = Color.White) in order to display the color, whenever if the TextField is focused or not.
This solution will remove the line, but also the cursor indicator. It's not the best for the moment but it's also the alpha actually on Compose.
TextField(
value = MyValue,
onValueChange = { },
textStyle = TextStyle(color = Color.White),
activeColor = Color.Transparent,
inactiveColor = Color.Transparent,
shape = RoundedCornerShape(20)
)