What is the Compose UI equivalent of `com.google.a...
# compose
t
What is the Compose UI equivalent of
com.google.android.material.textfield.TextInputLayout
incl. hint outline, character limit text, error handling? Is there any recommendable open source library out there?
t
OutlinedTextField
t
Yes,
OutlinedTextField
is what I started off with, too. But it does not offer the supported text below, automatic error handling, ... and maybe more that I do not think off right now. I can imagine many devs deals with the same scenario. Hence it would be great to use a commit library.
t
What do you mean by "text below" and "automatic error handling" ?
For showing error state there's the
isError
flag
For the text below there's many ways to do it: https://stackoverflow.com/q/68573228/6022725
t
Thanks. The
TextInputLayout
automatically shows the character count and max character limit below.
Here the text area is named "supporting text": https://m3.material.io/components/text-fields/specs
a
OutlinedTextField has supporting text in M3 cc https://issuetracker.google.com/227146125
t
How can I apply
outlinedTextFieldColors
which I use for the
OutlinedTextField
also to a
Text
defined in
supportingText = { ... }
so that error colors and such actually show up?
a
Have you tried and didnt work? because it should.
Copy code
OutlinedTextField(
    colors = TextFieldDefaults.outlinedTextFieldColors(errorSupportingTextColor = Cyan),
    value = text,
    onValueChange = {
        text = it
        validate(text)
    },
    singleLine = true,
    label = { Text(if (isError) "Username*" else "Username") },
    supportingText = {
        Text(
            modifier = Modifier.fillMaxWidth(),
            text = "Limit: ${text.length}/$charLimit",
            textAlign = TextAlign.End,
        )
    },
    isError = isError,
    keyboardActions = KeyboardActions { validate(text) })
Thats how you manually set errorSupportingTextColor in OutlinedTextField and its picked up by the Text inside supporting text If you dont do anything, it shows the default colour
t
This is what I tried (apart from defining more custom colors). Some colors work - the error color not.
a
Can you share a min repro project to take a look at? it should work
t
Sure:
Copy code
@ExperimentalMaterial3Api
@Composable
fun outlinedTextColors() = outlinedTextFieldColors(
    textColor = baseTextColor(),
    unfocusedBorderColor = baseTextColor(),
    unfocusedLabelColor = baseTextColor(),
    focusedLabelColor = baseTextColor(),
    placeholderColor = colorResource(R.color.tertiaryTextColor),
    focusedTrailingIconColor = baseIconColor(),
    focusedSupportingTextColor = colorResource(R.color.red),
    unfocusedSupportingTextColor = colorResource(R.color.red),
    disabledSupportingTextColor = colorResource(R.color.red),
    errorSupportingTextColor = colorResource(R.color.red),
    errorLabelColor = colorResource(R.color.red),
    errorBorderColor = colorResource(R.color.red),
)


@ExperimentalMaterial3Api
@Composable
private fun MyOutlinedTextField(
    configuration: Configuration,
    onTextChanged: (String) -> Unit,
    modifier: Modifier = Modifier,
) {
    var limitedText by remember { mutableStateOf(configuration.text) }
    var reachedTextLimit by remember { mutableStateOf(false) }
    val onClearText: () -> Unit = {
        limitedText = ""
        reachedTextLimit = false
    }

    OutlinedTextField(
        modifier = modifier,
        label = { BaseText(configuration.label, subheadlineMedium) },
        value = limitedText,
        onValueChange = {
            reachedTextLimit = it.length + 1 > configuration.textLimit.toInt()
            limitedText = if (reachedTextLimit) it.substring(0 until configuration.textLimit.toInt()) else it
            onTextChanged(it)
        },
        textStyle = subheadlineMedium,
        colors = outlinedTextColors(),
        isError = reachedTextLimit,
        singleLine = true,
        trailingIcon = {
            if (limitedText.isNotEmpty()) {
                ForgetIcon { onClearText() }
            }
        },
        supportingText = {
            Row {
                if (reachedTextLimit && configuration.reachedTextLimitMessage.isNotEmpty()) {
                    // TODO errorSupportingTextColor is ignored
                    BaseText(configuration.reachedTextLimitMessage, footnoteMedium, overflow = TextOverflow.Ellipsis)
                } else {
                    BaseText(
                        modifier = Modifier.fillMaxWidth(),
                        text = "${limitedText.length} / ${configuration.textLimit}",
                        textAlign = TextAlign.End,
                        style = footnoteMedium.copy(
                            colorResource(R.color.secondaryTextColor)
                        )
                    )
                }
            }
        },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done)
    )
}
a
if footnoteMedium redefines a text colour that will override errorSupportingTextColor. other than that I am getting errorSupportingTextColor with your code but i had to make changes to make it compile. is BaseText a class of top of Text? Unrelated, maybe you dont need a Row inside supportingText
t
True, that could be the reason. I will check.
Agreed, the Row might be needless. I haven't decided if I want to show the char count at the same time as the error message, though.
That works. Thank you.
BaseText
did overwrite the color. I did not see it immediately.