Android Compose TextField: How to set exact 3 lines

Viewed 2734

I want to create TextField with exact 3 lines:

destination

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?

2 Answers

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.

enter image description here

Related