I'm trying to migrate an existing `TextInputLayout...
# compose-android
a
I'm trying to migrate an existing
TextInputLayout/TextInputEditText
(see image) to a Composable that looks like an
OutlinedTextField
, but its
label
should be within the outlines instead of being on the outlines themselves, when the user types something on the text field. I've come up with something that uses
BasicTextField2
with
TextFieldDefaults.DecorationBox
as its decorator:
Copy code
@Composable
fun CustomTextField(
    isError: Boolean,
    textFieldState: TextFieldState,
    labelResId: Int,
    modifier: Modifier = Modifier
) {
    val borderColor = if (isError) {
        Error900
    } else {
        AccentBlue50
    }
    BasicTextField2(
        modifier = modifier
            .border(
                width = 1.dp,
                color = borderColor,
                RoundedCornerShape(4.dp)
            ),
        state = textFieldState,
        lineLimits = TextFieldLineLimits.SingleLine,
        textStyle = LocalTypography.current.body,
        decorator = {
            Column(Modifier.padding(4.dp)) {
                TextFieldDefaults.DecorationBox(
                    value = textFieldState.text.toString(),
                    innerTextField = it,
                    enabled = true,
                    singleLine = true,
                    visualTransformation = VisualTransformation.None,
                    interactionSource = remember { MutableInteractionSource() },
                    isError = false,
                    label = {
                        Text(
                            stringResource(id = labelResId),
                            style = LocalTypography.current.body,
                            color = SandGray
                        )
                    },
                    leadingIcon = null,
                    trailingIcon = null,
                    colors =
                        TextFieldDefaults.colors(
                            focusedContainerColor = Color.White,
                            unfocusedContainerColor = Color.White,
                            focusedIndicatorColor = Color.Transparent,
                            unfocusedIndicatorColor = Color.Transparent,
                            focusedTextColor = DarkGray,
                            unfocusedTextColor = DarkGray
                        ),
                    contentPadding = TextFieldDefaults.contentPaddingWithLabel(),
                )
            }
        }
    )
}
So far, it looks okay when unfocused, but I want the label to fully animate to its final position as soon as the text field gains focus (see video). This animation only happens when the user begins to type. Has anyone here done something similar?
Also, our iOS app has something like this and would love to have something similar:
Managed to get something working by not using
DecorationBox
, and just controlling the visibility of two
Texts
as label and placeholder:
Copy code
@Composable
fun CustomTextField(
    isError: Boolean,
    textFieldState: TextFieldState,
    labelResId: Int,
    modifier: Modifier = Modifier
) {
    val borderColor = if (isError) Error900 else AccentBlue50
    var isPlaceholderVisible by remember { mutableStateOf(true) }
    Box(
        modifier = modifier
            .onFocusChanged { if (it.isFocused) isPlaceholderVisible = false }
            .border(width = 1.dp, color = borderColor, RoundedCornerShape(4.dp))
            .padding(vertical = 14.dp, horizontal = 15.dp)
        ,
        contentAlignment = Alignment.Center
    ) {
        if (isPlaceholderVisible) {
            Text(
                modifier = Modifier
                    .fillMaxWidth().padding(bottom = 2.dp),
                text = stringResource(id = labelResId),
                style = LocalTypography.current.body,
                color = SandGray,
                textAlign = TextAlign.Start
            )
        }
        Column(horizontalAlignment = Alignment.Start) {
            if (!isPlaceholderVisible) {
                Text(
                    modifier = Modifier
                        .fillMaxWidth(),
                    text = stringResource(id = labelResId),
                    style = LocalTypography.current.smallNormal,
                    color = SandGray,
                    textAlign = TextAlign.Start
                )
            }
            BasicTextField2(
                modifier = Modifier.fillMaxWidth(),
                state = textFieldState,
                lineLimits = TextFieldLineLimits.SingleLine,
                textStyle = LocalTypography.current.body
            )
        }
    }
}