56

Is there any out of the box solution for limiting the character size in TextField's ? I don't see any maxLength parameter like we had in XML.

0

9 Answers 9

109

You can use the onValueChange parameter to limit the number of characters.

var text by remember { mutableStateOf("") }
val maxChar = 5

TextField(
    value = text,
    onValueChange = {
        if (it.length <= maxChar) text = it   
    }
    singleLine = true,
)

Then with M3 you can use the supportingText attribute to display the counter text.
Something like:

val maxChar = 5

TextField(
    value = text,
    onValueChange = {
        if (it.length <= maxChar) text = it
    },
    modifier = Modifier.fillMaxWidth(),
    supportingText = {
        Text(
            text = "${text.length} / $maxChar",
            modifier = Modifier.fillMaxWidth(),
            textAlign = TextAlign.End,
        )
    },
)

enter image description here

With M2 there isn't a built-in parameter.
In this case to display the counter text you can use something like:

val maxChar = 5

Column(){
    TextField(
        value = text,
        onValueChange = {
            if (it.length <= maxChar) text = it
        },
        singleLine = true,
        modifier = Modifier.fillMaxWidth()
    )
    Text(
        text = "${text.length} / $maxChar",
        textAlign = TextAlign.End,
        style = MaterialTheme.typography.caption,
        modifier = Modifier.fillMaxWidth().padding(end = 16.dp)
    )
}

enter image description here

Sign up to request clarification or add additional context in comments.

7 Comments

When using your snippet, if you type maxChar chars it works fine. Then for next char it won't show any new char (correct behaviour) but when typing another char (7th for maxChar = 5) it clears TextField and allows for new char inputs. Any explanation why it behaves like this and maybe possible fix?
Will pasting a long text work with implementation? Isn't text = it.take(maxChar) better?
@adek111 I am facing the same issue. Did you found any solution to it?
@Sahil, unfortunately not, still waiting for explanation from Gabriele
@adek111 I am facing the same issue. Did you find any solution to it?
|
19

You can use take function - here documentation

onValueChange = { onYearChanged(it.take(limitNum)) })

For example, if you will use it in function.

const val limitNum = 4

@Composable
fun YearRow(
  modifier: Modifier = Modifier,
  year: Int, 
  onYearChanged: (String) -> Unit,
) {
  OutlinedTextField(
    modifier = modifier,
    value = if (year == 0) "" else "$year",
    onValueChange = { onYearChanged(it.take(limitNum)) },
  )
}

Comments

8

The first answer to this question works fine, but it´s true that in some cases there is an error that when exceeding the number of characters allowed, the value of the texfield is cleared. This failure seems to be due to predictive text, because if predictive text is disabled in android, it does not happen. One solution I found for now as a workaround is to use focusManager to "limit writing".

First, we need to get the focus manager to control the focus on the screen. We can do this, by adding this line inside our composable function:

val focusManager = LocalFocusManager.current

Then, in our TextField we can use the focusManager to avoid the user to write more than the maxChar limit. We can move the focus to the next element, clear the focus when the maxChar limit is exceeded or receive a lambda function and perform the action we want . That depends on us.

var text by remember { mutableStateOf(TextFieldValue("")) }
val maxChar = 10

TextField(
    singleLine = true,
    value = text,
    onValueChange = {
        // This line will take (in case the user try to paste a text from the clipboard) only the allowed amount of characters
        text = it.take(maxChar)
        if (it.length > maxChar){
           focusManager.moveFocus(FocusDirection.Down) // Or receive a lambda function
        }
    }
)

In this way the user could never write more characters than what is established by the limit. Obviously, this is an alternative solution, which in my case solved my problem, now we have to wait to see if they add it natively

Comments

8

Trim the most recently inserted character according to selection, if the new string exceeds the length.

fun TextFieldValue.ofMaxLength(maxLength: Int): TextFieldValue {
    val overLength = text.length - maxLength
    return if (overLength > 0) {
        val headIndex = selection.end - overLength
        val trailIndex = selection.end
        // Under normal conditions, headIndex >= 0
        if (headIndex >= 0) {
            copy(
                text = text.substring(0, headIndex) + text.substring(trailIndex, text.length),
                selection = TextRange(headIndex)
            )
        } else {
            // exceptional
            copy(text.take(maxLength), selection = TextRange(maxLength))
        }
    } else {
        this
    }
}

Usage:

val (phone, setPhone) = remember {
    mutableStateOf(TextFieldValue())
}

PaddingTextField(
    value = phone,
    onValueChange = { newPhone ->
        setPhone(newPhone.ofMaxLength(11))
    }
)

1 Comment

Nice. This solution supports pasting and entering characters in the middle of the string, whereas the currently selected one would just delete characters from the end of the content.
2

Logic that will allow your user to hit the max length and then delete after

Code:

@Composable
fun CustomTextField(
    streamTitle:String,
    updateText:(String)->Unit
){
val maxlength = 141
TextField(
    value = streamTitle,
    onValueChange = {
    if (streamTitle.length <= maxlength || it.length < streamTitle.length) {
                 updateText(it)
      }

}

Explanation

  • streamTitle.length <= maxlength is the typical logic that will stop the user from entering text once the maxlength is hit. This has a problem that it will NOT allow the user to delete any text due to the maxLength being hit.

  • The additional logic of it.length < streamTitle.length is needed to combat the previously mentioned scenario. When a user deletes a character onValueChange() is called with new value of it minus the character deleted. Meaning that if our user has reached maxlength they will trigger our second conditional and now be allowed to delete characters

1 Comment

not working text field just clear value after reach max length
1

When using material3 TextField, you can leverage inputTransformation:

import androidx.compose.material3.TextField
import androidx.compose.foundation.text.input.InputTransformation


val maxLength = 10
TextField(
    state = rememberTextFieldState(),
    inputTransformation = InputTransformation.maxLength(maxLength),
)

Comments

0
@Composable
fun CustomOutlinedTextField(
    state: ErrorTextFieldState,
    label: String,
    maxChar: Int
) {
    val error = state.error
    OutlinedTextField(
        value = state.text,
        onValueChange = {
            state.updateText(it.take(maxChar))
        },
        textStyle = TextStyle(
            color = Color.Black,
            fontSize = 16.sp,
            fontFamily = FontFamily(Font(R.font.poppins))
        ),
        label = {
            Text(
                text = label,
                style = TextStyle(
                    fontSize = 14.sp,
                    fontFamily = FontFamily(Font(R.font.poppins))
                )
            )
        },
        modifier = Modifier.padding(
            start = 16.dp,
            end = 16.dp,
            top = 16.dp
           ).fillMaxWidth(),
        maxLines = 1,
        isError = error != null || state.text.length == maxChar,
        supportingText = {
            if (error != null || state.text.length == maxChar) {
                Text(
                    modifier = Modifier.fillMaxWidth(),
                    textAlign = if(error != null) TextAlign.Start else TextAlign.End,
                    text = if(error != null) error else "${state.text.length} / $maxChar",
                    color = MaterialTheme.colorScheme.error
                )
            } else {
                Text(
                    modifier = Modifier.fillMaxWidth(),
                    textAlign = TextAlign.End,
                    color = Color.Gray,
                    text = "${state.text.length} / $maxChar"
                )
            }
        },
        trailingIcon = {
            if (error != null) {
                Icon(Icons.Filled.Warning, "error", tint = MaterialTheme.colorScheme.error)
            }
        },
        shape = RoundedCornerShape(10.dp),
        colors = TextFieldDefaults.outlinedTextFieldColors(
            textColor = Color.Black,
            cursorColor = Color.Black,
            focusedBorderColor = Color.Black,
            unfocusedBorderColor = Color.Gray
        )
    )
}

"ErrorTextFieldState" custom class for processing input data

Using take(maxChar) in onValueChange does not allow you to enter more characters than specified in maxChar

enter image description here enter image description here

enter image description here

Comments

-3
onValueChange = { if (it.length < 20) {name = it} },

Comments

-5

Another way to do this that could be considered more flexible is something like:

Text(
    text = "A string with a lot of charsssssssssssssssssssssssssss"
    modifier = Modifier.fillMaxWidth(.5f),
    maxLines = 1,
    overflow = TextOverflow.Ellipsis
)

this will constraint the width with the fillMaxWidth bit and the height with the maxLines part. If both of those constraints are hit the text will overflow and the behavior for overflow can be specified

in this case once the text occupied half of the view or went more than one line it would end up something like A string with a lot of charsssss...

1 Comment

The question is asking about max text length for an input field, not max width

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.