31

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?

Search Field

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
DelusionaL
  • 745
  • 2
  • 6
  • 15
  • The `TextField` is a material component with some specs. If you want to change the height, you have to reduce the `TextSize`. Otherwise build your own `BasicTextField` removing some paddings. – Gabriele Mariotti May 25 '21 at 05:53
  • There is no option like TextSize but TextStyle. And yeah BasicTextField is the last option I hope. – DelusionaL May 25 '21 at 10:45

7 Answers7

27

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:

compose custom TextField

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(
                    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"
)
Zoran
  • 1,502
  • 19
  • 22
11

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
        )
    )
}

enter image description here

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
4

A simpler way would be to scale it:

TextField(
        value = "",
        onValueChange = {},
        modifier = Modifier.scale(scaleY = 0.9F, scaleX = 1F),
    )
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
Thanoss
  • 439
  • 5
  • 11
  • This is quite an elegant solution, but leep in mind that this will also scale the text. Scaling the textfield is kind of like making an image smaller by dragging the top down. – Henrik Klev Jun 17 '22 at 11:54
  • omg, delete this answer, we are talking about size of shape, not about scaling/resizing everything – user924 Aug 15 '23 at 10:55
2

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
            )
        }
    )
}
1

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()
                }
            }
        )
    }

0

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)
                )
            }
        ))
    }
}
Jeeva
  • 3,975
  • 3
  • 23
  • 47
0

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
                )
            )
        }
Junheng
  • 91
  • 1
  • 3