Related
I want to customize TextField composable in Jetpack Compose. I am trying to achieve the result in the image below, but somehow TextField has some default paddings which i couldn't find how to change values of. I want to remove default paddings and customize it
(The image on the right one is the result i achieved. I drew a border so that you can see it has padding, btw below that TextField are just Text composables, they aren't TextFields)
Below is my TextField code
TextField(
value = "",
onValueChange = {},
modifier = Modifier
.weight(1F)
.padding(0.dp)
.border(width = 1.dp, color = Color.Red),
placeholder = {
Text(
"5555 5555 5555 5555", style = TextStyle(
color = Color.Gray
)
)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
)
You can use BasicTextField, it's a plain text field without any decorations. Note that it doesn't have placeholder/hint too, you have to implement those by yourself if you need.
BasicTextField(value = "", onValueChange = {}, Modifier.fillMaxWidth())
Since 1.2.0-alpha04 it's much easier to make your BasicTextField look like TextField or OutlinedTextField. You can copy source code of TextField, which is pretty short since most of logic was moved into TextFieldDefaults.TextFieldDecorationBox, and pass the needed padding value into contentPadding parameter of TextFieldDefaults.TextFieldDecorationBox.
In the latest alpha release (androidx.compose.material:material:1.2.0-alpha04) they exposed TextFieldDefaults.TextFieldDecorationBox.
This is the implementation of the decorationBox composable used in the material TextField implementation.
You can use it as follows:
val interactionSource = remember { MutableInteractionSource() }
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
visualTransformation = visualTransformation,
interactionSource = interactionSource,
enabled = enabled,
singleLine = singleLine,
) { innerTextField ->
TextFieldDefaults.TextFieldDecorationBox(
value = value,
visualTransformation = visualTransformation,
innerTextField = innerTextField,
singleLine = singleLine,
enabled = enabled,
interactionSource = interactionSource,
contentPadding = PaddingValues(0.dp), // this is how you can remove the padding
)
}
This will allow you to remove the padding but still get the rest of the features that come with TextField.
Remember to use the same MutableInteractionSource for both the BasicTextField and the TextFieldDefaults.TextFieldDecorationBox.
The official documentation I linked to above shows more examples if its usage.
Thank you all, i did use BasicTextField and achieved the result i wanted :)
#Composable
fun BottomOutlineTextField(placeholder: String, value: String, onValueChange: (String) -> Unit) {
BasicTextField(
modifier = Modifier.fillMaxWidth(),
value = value,
onValueChange = onValueChange,
textStyle = TextStyle(
color = if (isSystemInDarkTheme()) Color(0xFF969EBD) else Color.Gray
),
decorationBox = { innerTextField ->
Row(modifier = Modifier.fillMaxWidth()) {
if (value.isEmpty()) {
Text(
text = placeholder,
color = if (isSystemInDarkTheme()) Color(0xFF969EBD) else Color.Gray,
fontSize = 14.sp
)
}
}
innerTextField()
}
)
}
I wanted to cut off the 16.dp at the start. I managed to this in the following way:
BoxWithConstraints(modifier = Modifier
.clipToBounds()
) {
TextField(modifier = Modifier
.requiredWidth(maxWidth+16.dp)
.offset(x=(-8).dp))
}
I solved this problem by coping all source code from TextField and replace this lines of code :
val paddingToIcon = TextFieldPadding - HorizontalIconPadding
// val padding = Modifier.padding(
// start = if (leading != null) paddingToIcon else TextFieldPadding,
// end = if (trailing != null) paddingToIcon else TextFieldPadding
// )
val padding = Modifier.padding(
start = if (leading != null) paddingToIcon else 0.dp,
end = if (trailing != null) paddingToIcon else 0.dp
)
And this work great!
You can put your TextField in a Box and apply modifiers, e.g.
Box(
modifier = Modifier.background(
shape = RoundedCornerShape(percent = 10),
color = colorBackgroundGray
)
) {
TextField(
value = text,
onValueChange = onValueChange,
modifier = Modifier.fillMaxWidth().padding(8.dp, 0.dp, 0.dp, 0.dp)...
}
Using
...
decorationBox{
TextFieldDefaults.TextFieldDecorationBox(
....
)
required me to add the anotation #OptIn(ExperimentalMaterialApi::class) which seems to not be a good aproach for a release scenario.
Instead, I could get the result I as expecteing by the following implementation:
#Composable
fun Input(
text: TextFieldValue = TextFieldValue(),
onValueChanged: (TextFieldValue) -> Unit = { },
keyboardType: KeyboardType = KeyboardType.Text,
imeAction: ImeAction = ImeAction.Done,
isEnable: Boolean = true,
singleLine: Boolean = true,
shape: Shape = rounded,
autoCorrect: Boolean = false,
innerPadding: PaddingValues = PaddingValues(15.dp, 7.dp)
) {
val focusManager = LocalFocusManager.current
val fontSize = 16.sp
BasicTextField(
value = text,
onValueChange = onValueChanged,
Modifier
.clip(shape)
.border(1.dp, Color.Gray, shape)
.background(Color.White),
textStyle = TextStyle(color = Color.Black, fontSize = fontSize),
enabled = isEnable,
singleLine = singleLine,
keyboardOptions = KeyboardOptions(
KeyboardCapitalization.None,
autoCorrect,
keyboardType,
imeAction
),
keyboardActions = KeyboardActions(
onAny = {
focusManager.clearFocus()
}
),
decorationBox = {
Box(
modifier = Modifier.padding(innerPadding)
) {
if(text.text.isBlank()) {
Text(
text = "Search",
style = TextStyle(color = Color.Black, fontSize = fontSize)
)
}
it()
}
}
)
}
Hope it helps somebody.
It is so easy, just copy all TextField or OutlinedTextField functions and remove defaultMinSize then add contentPadding
After that, you can use it instead of main TextField or OutlinedTextField
for OutlinedTextField:
#Composable
fun BorderTextField(
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,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
) {
// If color is not provided via the text style, use content color as a default
val textColor = textStyle.color.takeOrElse {
colors.textColor(enabled).value
}
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
#OptIn(ExperimentalMaterialApi::class)
BasicTextField(
value = value,
modifier = if (label != null) {
modifier
// Merge semantics at the beginning of the modifier chain to ensure padding is
// considered part of the text field.
.semantics(mergeDescendants = true) {}
} else {
modifier
}
.background(colors.backgroundColor(enabled).value, shape),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = mergedTextStyle,
cursorBrush = SolidColor(colors.cursorColor(isError).value),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
decorationBox = #Composable { innerTextField ->
TextFieldDefaults.OutlinedTextFieldDecorationBox(
value = value,
visualTransformation = visualTransformation,
innerTextField = innerTextField,
placeholder = placeholder,
label = label,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
singleLine = singleLine,
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
colors = colors,
contentPadding = TextFieldDefaults.outlinedTextFieldPadding(
start = 0.dp,
top = 0.dp,
end = 0.dp,
bottom = 0.dp
),
border = {
TextFieldDefaults.BorderBox(
enabled,
isError,
interactionSource,
colors,
shape
)
}
)
}
)
}
Actually that is innate, it follows material guidelines. If you wish to disable it, an alternative could be to set the height of the TextField explicitly, and matching it with the font size of the text. That way it will only extend till the text does. Another way would be to look at the source of TextField. You could just copy the source and make modifications to meet your requirements. The former sounds like an easy fix, however, the latter is no big deal as well. It is doable, and is a recommended practice to customize behavior for your needs. Also, just as a side note, I don't think it is a good idea to disable that padding. It was added to design guidelines since it seems pretty sensible and natural to have it. Sometimes we find some designs attractive when we think about them but they aren't as good when seen implemented.
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 want to customize TextField composable in Jetpack Compose. I am trying to achieve the result in the image below, but somehow TextField has some default paddings which i couldn't find how to change values of. I want to remove default paddings and customize it
(The image on the right one is the result i achieved. I drew a border so that you can see it has padding, btw below that TextField are just Text composables, they aren't TextFields)
Below is my TextField code
TextField(
value = "",
onValueChange = {},
modifier = Modifier
.weight(1F)
.padding(0.dp)
.border(width = 1.dp, color = Color.Red),
placeholder = {
Text(
"5555 5555 5555 5555", style = TextStyle(
color = Color.Gray
)
)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
)
You can use BasicTextField, it's a plain text field without any decorations. Note that it doesn't have placeholder/hint too, you have to implement those by yourself if you need.
BasicTextField(value = "", onValueChange = {}, Modifier.fillMaxWidth())
Since 1.2.0-alpha04 it's much easier to make your BasicTextField look like TextField or OutlinedTextField. You can copy source code of TextField, which is pretty short since most of logic was moved into TextFieldDefaults.TextFieldDecorationBox, and pass the needed padding value into contentPadding parameter of TextFieldDefaults.TextFieldDecorationBox.
In the latest alpha release (androidx.compose.material:material:1.2.0-alpha04) they exposed TextFieldDefaults.TextFieldDecorationBox.
This is the implementation of the decorationBox composable used in the material TextField implementation.
You can use it as follows:
val interactionSource = remember { MutableInteractionSource() }
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
visualTransformation = visualTransformation,
interactionSource = interactionSource,
enabled = enabled,
singleLine = singleLine,
) { innerTextField ->
TextFieldDefaults.TextFieldDecorationBox(
value = value,
visualTransformation = visualTransformation,
innerTextField = innerTextField,
singleLine = singleLine,
enabled = enabled,
interactionSource = interactionSource,
contentPadding = PaddingValues(0.dp), // this is how you can remove the padding
)
}
This will allow you to remove the padding but still get the rest of the features that come with TextField.
Remember to use the same MutableInteractionSource for both the BasicTextField and the TextFieldDefaults.TextFieldDecorationBox.
The official documentation I linked to above shows more examples if its usage.
Thank you all, i did use BasicTextField and achieved the result i wanted :)
#Composable
fun BottomOutlineTextField(placeholder: String, value: String, onValueChange: (String) -> Unit) {
BasicTextField(
modifier = Modifier.fillMaxWidth(),
value = value,
onValueChange = onValueChange,
textStyle = TextStyle(
color = if (isSystemInDarkTheme()) Color(0xFF969EBD) else Color.Gray
),
decorationBox = { innerTextField ->
Row(modifier = Modifier.fillMaxWidth()) {
if (value.isEmpty()) {
Text(
text = placeholder,
color = if (isSystemInDarkTheme()) Color(0xFF969EBD) else Color.Gray,
fontSize = 14.sp
)
}
}
innerTextField()
}
)
}
I wanted to cut off the 16.dp at the start. I managed to this in the following way:
BoxWithConstraints(modifier = Modifier
.clipToBounds()
) {
TextField(modifier = Modifier
.requiredWidth(maxWidth+16.dp)
.offset(x=(-8).dp))
}
I solved this problem by coping all source code from TextField and replace this lines of code :
val paddingToIcon = TextFieldPadding - HorizontalIconPadding
// val padding = Modifier.padding(
// start = if (leading != null) paddingToIcon else TextFieldPadding,
// end = if (trailing != null) paddingToIcon else TextFieldPadding
// )
val padding = Modifier.padding(
start = if (leading != null) paddingToIcon else 0.dp,
end = if (trailing != null) paddingToIcon else 0.dp
)
And this work great!
You can put your TextField in a Box and apply modifiers, e.g.
Box(
modifier = Modifier.background(
shape = RoundedCornerShape(percent = 10),
color = colorBackgroundGray
)
) {
TextField(
value = text,
onValueChange = onValueChange,
modifier = Modifier.fillMaxWidth().padding(8.dp, 0.dp, 0.dp, 0.dp)...
}
Using
...
decorationBox{
TextFieldDefaults.TextFieldDecorationBox(
....
)
required me to add the anotation #OptIn(ExperimentalMaterialApi::class) which seems to not be a good aproach for a release scenario.
Instead, I could get the result I as expecteing by the following implementation:
#Composable
fun Input(
text: TextFieldValue = TextFieldValue(),
onValueChanged: (TextFieldValue) -> Unit = { },
keyboardType: KeyboardType = KeyboardType.Text,
imeAction: ImeAction = ImeAction.Done,
isEnable: Boolean = true,
singleLine: Boolean = true,
shape: Shape = rounded,
autoCorrect: Boolean = false,
innerPadding: PaddingValues = PaddingValues(15.dp, 7.dp)
) {
val focusManager = LocalFocusManager.current
val fontSize = 16.sp
BasicTextField(
value = text,
onValueChange = onValueChanged,
Modifier
.clip(shape)
.border(1.dp, Color.Gray, shape)
.background(Color.White),
textStyle = TextStyle(color = Color.Black, fontSize = fontSize),
enabled = isEnable,
singleLine = singleLine,
keyboardOptions = KeyboardOptions(
KeyboardCapitalization.None,
autoCorrect,
keyboardType,
imeAction
),
keyboardActions = KeyboardActions(
onAny = {
focusManager.clearFocus()
}
),
decorationBox = {
Box(
modifier = Modifier.padding(innerPadding)
) {
if(text.text.isBlank()) {
Text(
text = "Search",
style = TextStyle(color = Color.Black, fontSize = fontSize)
)
}
it()
}
}
)
}
Hope it helps somebody.
It is so easy, just copy all TextField or OutlinedTextField functions and remove defaultMinSize then add contentPadding
After that, you can use it instead of main TextField or OutlinedTextField
for OutlinedTextField:
#Composable
fun BorderTextField(
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,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
) {
// If color is not provided via the text style, use content color as a default
val textColor = textStyle.color.takeOrElse {
colors.textColor(enabled).value
}
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
#OptIn(ExperimentalMaterialApi::class)
BasicTextField(
value = value,
modifier = if (label != null) {
modifier
// Merge semantics at the beginning of the modifier chain to ensure padding is
// considered part of the text field.
.semantics(mergeDescendants = true) {}
} else {
modifier
}
.background(colors.backgroundColor(enabled).value, shape),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = mergedTextStyle,
cursorBrush = SolidColor(colors.cursorColor(isError).value),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
decorationBox = #Composable { innerTextField ->
TextFieldDefaults.OutlinedTextFieldDecorationBox(
value = value,
visualTransformation = visualTransformation,
innerTextField = innerTextField,
placeholder = placeholder,
label = label,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
singleLine = singleLine,
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
colors = colors,
contentPadding = TextFieldDefaults.outlinedTextFieldPadding(
start = 0.dp,
top = 0.dp,
end = 0.dp,
bottom = 0.dp
),
border = {
TextFieldDefaults.BorderBox(
enabled,
isError,
interactionSource,
colors,
shape
)
}
)
}
)
}
Actually that is innate, it follows material guidelines. If you wish to disable it, an alternative could be to set the height of the TextField explicitly, and matching it with the font size of the text. That way it will only extend till the text does. Another way would be to look at the source of TextField. You could just copy the source and make modifications to meet your requirements. The former sounds like an easy fix, however, the latter is no big deal as well. It is doable, and is a recommended practice to customize behavior for your needs. Also, just as a side note, I don't think it is a good idea to disable that padding. It was added to design guidelines since it seems pretty sensible and natural to have it. Sometimes we find some designs attractive when we think about them but they aren't as good when seen implemented.
Now I have a custom Compose input box. BasicTextField, but I can’t center the input font anyway. I can only offset in the x direction through the offset property of the Box.
The reason for customizing the input box is because I need a fixed height input box and font size. But the text field cannot be input when the height is not enough.
Please help me, I prefer to use TextField to write input boxes with a fixed height. But no suitable solution was found on the site.
Here is my custom code:
#Composable
fun InputEditText(
value: String,
modifier: Modifier,
onValueChange: (String) -> Unit,
contentTextStyle: TextStyle,
hintTextStyle: TextStyle,
placeHolderString: String = "",
enabled: Boolean = true,
readOnly: Boolean = false,
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
offsetDp: Dp = 10.dp,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
cursorColor: Color = Color.Black,
) {
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
textStyle = contentTextStyle,
decorationBox = {innerTextField ->
Box(
modifier = Modifier
.fillMaxWidth()
.offset(x = offsetDp),
contentAlignment = Alignment.CenterStart,
) {
if (value.isEmpty()) {
Text(
text = placeHolderString,
color = hintTextStyle.color,
fontSize = hintTextStyle.fontSize
)
}
innerTextField()
}
},
enabled = enabled,
readOnly = readOnly,
singleLine = singleLine,
maxLines = maxLines,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
cursorBrush = SolidColor(cursorColor)
)
}
You can use something like:
BasicTextField(
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
decorationBox = {innerTextField ->
Box(
contentAlignment = Alignment.Center
) {
if (text.isEmpty()) {
Text(
text = "placeHolder",
)
}
innerTextField()
}
}
)
So I have this textfield,
var value = remember { mutableStateOf("") }
OutlinedTextField(
value = value.value,
placeholder = {
Text("Search Users")
},
singleLine = true,
modifier = Modifier.height(40.dp),
onValueChange = {
value.value = it
},
)
I am setting height to 40.dp as you can see. However it looks like this,
Looks like the text/placeholder is cut off. How to fix this?
I run into same problem using OutlinedTextField. Obviously, this is material component that have exact padding, that is causing crop on text (even with smaller font size).
Here is result:
Solution is to use BasicTextField, and here is the code:
#Composable
private fun CustomTextField(
modifier: Modifier = Modifier,
leadingIcon: (#Composable () -> Unit)? = null,
trailingIcon: (#Composable () -> Unit)? = null,
placeholderText: String = "Placeholder",
fontSize: TextUnit = MaterialTheme.typography.body2.fontSize
) {
var text by rememberSaveable { mutableStateOf("") }
BasicTextField(modifier = modifier
.background(
MaterialTheme.colors.surface,
MaterialTheme.shapes.small,
)
.fillMaxWidth(),
value = text,
onValueChange = {
text = it
},
singleLine = true,
cursorBrush = SolidColor(MaterialTheme.colors.primary),
textStyle = LocalTextStyle.current.copy(
color = MaterialTheme.colors.onSurface,
fontSize = fontSize
),
decorationBox = { innerTextField ->
Row(
modifier,
verticalAlignment = Alignment.CenterVertically
) {
if (leadingIcon != null) leadingIcon()
Box(Modifier.weight(1f)) {
if (text.isEmpty()) Text(
placeholderText,
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.onSurface.copy(alpha = 0.3f),
fontSize = fontSize
)
)
innerTextField()
}
if (trailingIcon != null) trailingIcon()
}
}
)
}
calling it with changed background shape:
CustomTextField(
leadingIcon = {
Icon(
Icons.Filled.Search,
null,
tint = LocalContentColor.current.copy(alpha = 0.3f)
)
},
trailingIcon = null,
modifier = Modifier
.background(
MaterialTheme.colors.surface,
RoundedCornerShape(percent = 50)
)
.padding(4.dp)
.height(20.dp),
fontSize = 10.sp,
placeholderText = "Search"
)
Instead of using height you can set the minHeight:
modifier = Modifier.defaultMinSize(minHeight = 40.dp)
See the problem is that the font size is too big for the provided height. The correct way to provide a height for the field is using the modifiers as you already are doing. However, to work around this problem either increase the height of the text field or decrease the size of the font.
It's my material TextField with the ability to change contentPadding. Then you call anywhere.
#Composable
fun MyTextField(
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(),
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.TextFieldShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors(),
contentPadding: PaddingValues = PaddingValues(start = 10.dp, end = 10.dp, top = 5.dp, bottom = 5.dp)
) {
// If color is not provided via the text style, use content color as a default
val textColor = textStyle.color.takeOrElse {
colors.textColor(enabled).value
}
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
#OptIn(ExperimentalMaterialApi::class)
BasicTextField(
value = value,
modifier = modifier
.background(colors.backgroundColor(enabled).value, shape)
.indicatorLine(enabled, isError, interactionSource, colors),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = mergedTextStyle,
cursorBrush = SolidColor(colors.cursorColor(isError).value),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
decorationBox = #Composable { innerTextField ->
// places leading icon, text field with label and placeholder, trailing icon
TextFieldDefaults.TextFieldDecorationBox(
value = value,
visualTransformation = visualTransformation,
innerTextField = innerTextField,
placeholder = placeholder,
label = label,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
singleLine = singleLine,
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
colors = colors,
contentPadding = contentPadding
)
}
)
}