Reputation: 955
I am trying to design a search bar like Google search bar with decreased height. But the Input text is getting cropped also the placeholder text.
TextField(
value = searchText.value,
singleLine = true,
onValueChange = {
searchText.value = it
},
placeholder = { //placeholder text is also getting cropped
Text(
text = "Search",
fontSize = 20.sp,
)
},
textStyle = TextStyle(fontSize = 20.sp), // input text is getting cropped
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)
.height(45.dp), // Here I have decreased the height
shape = RoundedCornerShape(22.dp),
)
My Input text and Placeholder text getting 50% cropped. How to solve it?
Upvotes: 38
Views: 25966
Reputation: 185
val textFieldState = rememberTextFieldState()
CustomOutlinedTextField(
textFieldState.text.toString(),
{ textFieldState.setTextAndPlaceCursorAtEnd(it) },
shape = RoundedCornerShape(100.dp),
modifier = Modifier.height(40.dp),
textStyle = MaterialTheme.typography.bodyMedium,
leadingIcon = { Icon(painterResource(Res.drawable.magnifier), "") },
singleLine = true
)
You can modify the original code structure. Here's how I did it on compose version 1.7.1 for the OutlineTextField:
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.TextFieldColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomOutlinedTextField(
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,
prefix: @Composable (() -> Unit)? = null,
suffix: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
interactionSource: MutableInteractionSource? = null,
shape: Shape = OutlinedTextFieldDefaults.shape,
colors: TextFieldColors = OutlinedTextFieldDefaults.colors()
) {
@Suppress("NAME_SHADOWING")
val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
val textColor =
textStyle.color.takeOrElse {
val focused = interactionSource.collectIsFocusedAsState().value
colors.textColor(enabled, isError, focused)
MaterialTheme.colorScheme.onBackground
}
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
val density = LocalDensity.current
CompositionLocalProvider(LocalTextSelectionColors provides colors.textSelectionColors) {
BasicTextField(
value = value,
modifier =
modifier
.then(
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) {}
.padding(top = with(density) { OutlinedTextFieldTopPadding.toDp() })
} else {
Modifier
}
)
.defaultMinSize(
minWidth = OutlinedTextFieldDefaults.MinWidth,
minHeight = OutlinedTextFieldDefaults.MinHeight
),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = mergedTextStyle,
cursorBrush = SolidColor(colors.cursorColor(isError)),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
minLines = minLines,
decorationBox =
@Composable { innerTextField ->
OutlinedTextFieldDefaults.DecorationBox(
value = value,
visualTransformation = visualTransformation,
innerTextField = innerTextField,
placeholder = placeholder,
label = label,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
prefix = prefix,
suffix = suffix,
supportingText = supportingText,
singleLine = singleLine,
enabled = enabled,
isError = isError,
contentPadding = PaddingValues(top = 0.dp), // NEW CODE
interactionSource = interactionSource,
colors = colors,
container = {
OutlinedTextFieldDefaults.Container(
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
colors = colors,
shape = shape,
)
}
)
}
)
}
}
@Composable
private fun TextFieldColors.textColor(
enabled: Boolean,
isError: Boolean,
focused: Boolean,
): Color =
when {
!enabled -> disabledTextColor
isError -> errorTextColor
focused -> focusedTextColor
else -> unfocusedTextColor
}
private fun TextFieldColors.cursorColor(isError: Boolean): Color =
if (isError) errorCursorColor else cursorColor
private object TypeScaleTokens {
val BodySmallLineHeight = 16.0.sp
}
/**
* In the focused state, the top half of the label sticks out above the text field. This default
* padding is a best-effort approximation to keep the label from overlapping with the content above
* it. It is sufficient when the label is a single line and developers do not override the label's
* font size/style. Otherwise, developers will need to add additional padding themselves.
*/
private val OutlinedTextFieldTopPadding = TypeScaleTokens.BodySmallLineHeight / 2
Upvotes: 0
Reputation: 2238
I run into same problem using TextField. Obviously, this is material component that have exact padding, that is causing crop on text (even with smaller font size).
Here is result:
As comment suggested 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(
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"
)
Upvotes: 32
Reputation: 406
To fully replicate ordinary TextField
params and apply them to BasicTextField
so we can edit the height, this is what I came up with (including imports):
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SimpleTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
placeholder: @Composable (() -> Unit)? = null,
onTextLayout: (TextLayoutResult) -> Unit = {},
cursorBrush: Brush = SolidColor(Color.Black),
colors: TextFieldColors = TextFieldDefaults.textFieldColors(),
shape: Shape = RoundedCornerShape(8.dp),
) {
BasicTextField(modifier = modifier
.background(MaterialTheme.colors.onSurface, shape = shape),
value = value,
onValueChange = onValueChange,
singleLine = singleLine,
maxLines = maxLines,
enabled = enabled,
readOnly = readOnly,
interactionSource = interactionSource,
textStyle = textStyle,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
onTextLayout = onTextLayout,
cursorBrush = cursorBrush,
decorationBox = { innerTextField ->
TextFieldDefaults.TextFieldDecorationBox(
value = value,
innerTextField = {
Box(
modifier = Modifier.fillMaxHeight(),
contentAlignment = Alignment.CenterStart
) {
innerTextField()
}
},
enabled = enabled,
colors = colors,
singleLine = singleLine,
visualTransformation = VisualTransformation.None,
interactionSource = interactionSource,
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 0.dp,
bottom = 0.dp
),
placeholder = {
if (value.isEmpty() && placeholder != null) {
Box(
modifier = Modifier.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
placeholder()
}
}
},
leadingIcon = leadingIcon,
trailingIcon = trailingIcon
)
}
)
}
Upvotes: 5
Reputation: 319
You can try Modifier.requiredHeight properties. Here is the example.
Box(
modifier = Modifier
.clip(RoundedCornerShape(20.dp))
.height(40.dp)
.background(Color.LightGray)
) {
TextField(value = inputMsg, modifier = Modifier.requiredHeight(56.dp),
placeholder = {
Text(
text = "Talk with me."
)
}, shape = CircleShape, onValueChange = { inputMsg = it },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
)
)
}
Upvotes: -1
Reputation: 4865
for some reason, the accepted answer was crashing when you type and clear all text and typed again. So I copied the actual implementation of TextField
and changed the height and removed the padding.
@Composable
fun SearchTextField(
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,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.filledShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
) {
// If color is not provided via the text style, use content color as a default
val textColor = textStyle.color.takeOrElse {
MaterialTheme.colorScheme.onSurface
}
val customTextSelectionColors = TextSelectionColors(
handleColor = MaterialTheme.colorScheme.primary,
backgroundColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.4f)
)
CompositionLocalProvider(LocalTextSelectionColors provides customTextSelectionColors) {
@OptIn(ExperimentalMaterial3Api::class)
(BasicTextField(
value = value,
modifier = modifier.height(40.dp),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
minLines = minLines,
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,
supportingText = supportingText,
shape = shape,
singleLine = singleLine,
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
colors = colors,
contentPadding = PaddingValues(0.dp)
)
}
))
}
}
Upvotes: 0
Reputation: 364730
Starting with 1.2.0
you can use the TextFieldDecorationBox
with the BasicTextField
.
Here you can use the contentPadding
attribute to reduce the vertical padding:
val colors = TextFieldDefaults.textFieldColors()
BasicTextField(
value = text,
onValueChange = { text = it },
textStyle = TextStyle(fontSize = 20.sp),
modifier = Modifier
.background(
color = colors.backgroundColor(enabled).value,
shape = RoundedCornerShape(22.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 = text,
innerTextField = it,
singleLine = singleLine,
enabled = enabled,
visualTransformation = VisualTransformation.None,
trailingIcon = { /* ... */ },
placeholder = {
Text(
text = "Search",
fontSize = 20.sp,
)
},
interactionSource = interactionSource,
// keep horizontal paddings but change the vertical
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 0.dp, bottom = 0.dp
)
)
}
Upvotes: 15
Reputation: 563
i slightly improved @zoran code, this code have full BasicTextField parameters .
@Composable
fun SimpleTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
placeholderText: String = "",
fontSize: TextUnit = MaterialTheme.typography.body2.fontSize,
onTextLayout: (TextLayoutResult) -> Unit = {},
cursorBrush: Brush = SolidColor(Color.Black),
) {
BasicTextField(modifier = modifier
.fillMaxWidth(),
value = value,
onValueChange = onValueChange,
singleLine = singleLine,
maxLines = maxLines,
enabled = enabled,
readOnly = readOnly,
interactionSource = interactionSource,
textStyle = textStyle,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
onTextLayout = onTextLayout,
cursorBrush = cursorBrush,
decorationBox = { innerTextField ->
Row(
modifier,
verticalAlignment = Alignment.CenterVertically
) {
if (leadingIcon != null) leadingIcon()
Box(Modifier.weight(1f)) {
if (value.isEmpty()) Text(
placeholderText,
style = textStyle
)
innerTextField()
}
if (trailingIcon != null) trailingIcon()
}
}
)
}
Upvotes: 2
Reputation: 543
A simpler way would be to scale it:
TextField(
value = "",
onValueChange = {},
modifier = Modifier.scale(scaleY = 0.9F, scaleX = 1F),
)
Upvotes: 2