Jetpack Compose: Custom TextField design

12,254

Solution 1

By this exemple you can learn a lot. With 1.0.0 you can do like this:

Custom TextField printscreen here

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

Solution 2

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

enter image description here

Solution 3

Well, until the issue I mentioned is resolved, the choices are:

  1. Roll back to Compose version 1.0.0-alpha04 (issue was introduced in alpha05)
  2. Add a border to a TextField or OutlinedTextField as so:
    TextField(
        value = myValue,
        onValueChange = myOnChange,
        modifier = Modifier.clip(myShape).border(5.dp, myColor)
    )
    
Share:
12,254
foxtrotuniform6969
Author by

foxtrotuniform6969

Updated on June 06, 2022

Comments

  • foxtrotuniform6969
    foxtrotuniform6969 about 2 years

    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:

    Custom text input

    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?