TextField En Compose

En este tutorial veremos el componente TextField en Compose con el objetivo de crear campos de texto, que le permitan al usuario ingresar y editar texto desde la interfaz gráfica. Exploraremos los dos tipos existentes, sus comportamientos, elementos decorativos, la detección de eventos cuando se cambia el texto, etc.

Lectura: Te recomiendo leer Text En Compose ya que usaremos este componente para crear los campos de texto.


Ejemplo De TextField En Compose

Tomaremos como ilustración pequeños ejemplos asociados a las características en cada sección explicada. Para ello crearemos funciones componibles dentro de varios archivos al interior del paquete examples/TextField del módulo p7_componentes.

Ejemplos de TextField en Compose

Y al igual que los demás tutoriales de la guía de Compose, puedes encontrar el proyecto fuente en el repositorio de GitHub.


1. Tipos De Campos De Texto

Filled Text Field

La función componible de nivel superior TextField() representa al campo de texto lleno, el cual proyecta un contenedor con fondo gris claro con el fin de destacar de otros componentes con que se mezcle.

Si revisas su definición encontrarás la siguiente firma:

@Composable
fun TextField(
    value: String,
    onValueChange: (String) -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    textStyle: TextStyle = LocalTextStyle.current,
    label: () -> Unit = null,
    placeholder: () -> Unit = null,
    leadingIcon: () -> Unit = null,
    trailingIcon: () -> Unit = null,
    isError: Boolean = false,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions(),
    singleLine: Boolean = false,
    maxLines: Int = Int.MAX_VALUE,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    shape: Shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
    colors: TextFieldColors = TextFieldDefaults.textFieldColors()
): @Composable Unit

Claramente tenemos muchos atributos por explorar, pero comenzaremos con la forma más básica de TextField pasando el valor (value), controlador de cambio de texto (onValueChange) y etiqueta (label).

@Composable
fun TextFieldExample() {
    var name by remember {
        mutableStateOf("Carlos")
    }
    
    TextField(
        value = name,
        onValueChange = { name = it },
        label = { Text("Nombre") }
    )
}
Ejemplo de TextField en Compose

Debido a que es necesario actualizar el valor del texto del campo de texto en cada evento de tipeo, a value se le asigna el valor del estado actual y onValueChange lo actualiza. Lee mi tutorial Estado en Compose para profundizar este tema.

Outlined Text Field

El campo de texto delineado o OutlinedTextField, proyecta menos énfasis visual que el campo de texto lleno, ya que el fondo de su contenedor es transparente. Dicha naturaleza lo hace excelente candidato para formularios con múltiples campos de texto y pocos componentes de otros tipos.

Su definición en Compose es exactamente igual a la de TextField, por lo que en el ejemplo anterior puedes reemplazar la función por OutlinedTextField así:

@Composable
fun OutlinedTextFieldExample() {
    var name by remember {
        mutableStateOf("Carlos")
    }

    OutlinedTextField(
        value = name,
        onValueChange = { name = it },
        label = { Text("Nombre") }
    )
}
Ejemplo de OutlinedTextField en Compose

2. Texto De La Etiqueta

La etiqueta le indica al usuario qué información recibe el campo de texto. Su indicador comienza en la entrada de texto hasta que el campo de texto recibe el foco.

En el caso del filled text field, la etiqueta flota en el medio del contenedor:

Etiqueta de filled text field en Compose

Pero para el outlined text field flota hasta su trazo superior:

Etiqueta de outlined text field en Compose

Define la etiqueta a partir del parámetro label, el cual recibe un tipo función componible ()->Unit, en el que podrás especificar el componente Text con el mensaje de etiqueta.

Adicionalmente, si deseas que aparezca un texto indicativo cuando el campo está vacío y toma el foco, entonces usa el parámetro placeholder.

El siguiente código produce el campo de texto delineado anterior:

@Composable
fun LabelAndPlaceholderExample() {
    var address by remember {
        mutableStateOf("")
    }

    OutlinedTextField(
        value = address,
        onValueChange = { address = it },
        label = { Text("Dirección") },
        placeholder = { Text("¿Dónde vives?") }
    )
}

3. Elementos De Asistencia

Añadir Texto De Ayuda

A diferencia de su contraparte en el sistema de views (TextInputLayout), TextField no posee un parámetro asociado para mostrar un texto de ayuda en la parte inferior del campo de texto.

No obstante, es cuestión de que crees un layout Column para ubicar un Text por debajo.

@Composable
fun HelperTextExample() {
    var phone by remember {
        mutableStateOf("")
    }
    
    Column {
        TextField(
            value = phone,
            onValueChange = { phone = it },
            label = { Text("Teléfono*") }
        )
        Text(
            text = "*Obligatorio",
            color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
            style = MaterialTheme.typography.caption,
            modifier = Modifier.padding(start = 16.dp)
        )
    }
}
Helper text de TextField en Compose

Añadir Mensajes De Error

El mensaje de error reemplaza al texto de ayuda cuando la entrada del usuario en un campo de texto no es válida.

Ejemplo texto de error en TextField Compose

Para determinar si el campo de texto entró a estado de error usa el parámetro booleano isError. Al pasar true, Compose usará el color para errores definido en tu tema para colorear al componente.

Por ejemplo: Validar campo requerido al presionar un botón.

@Composable
fun ErrorTextExample() {
    var name by remember { mutableStateOf("") }
    var nameError by remember { mutableStateOf(false) } // 1

    Column {
        TextField(
            value = name,
            onValueChange = {
                name = it
                nameError = false // 2
            },
            label = { Text("Nombre") },
            isError = nameError // 3
        )

        val assistiveElementText = if (nameError) "Error: Obligatorio" else "*Obligatorio" // 4
        val assistiveElementColor = if (nameError) { // 5
            MaterialTheme.colors.error
        } else {
            MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium)
        }

        Text(// 6
            text = assistiveElementText,
            color = assistiveElementColor,
            style = MaterialTheme.typography.caption,
            modifier = Modifier.padding(start = 16.dp)
        )

        Spacer(Modifier.size(16.dp))

        Button(
            onClick = { nameError = name.isBlank() }, // 7
            modifier = Modifier.align(Alignment.CenterHorizontally)
        ) {
            Text("GUARDAR")
        }
    }
}

En el código anterior:

  1. Declaramos un estado para la existencia del error del campo de nombre
  2. Limpiamos el error cuando se modifique el texto
  3. Asignamos el estado del error al parámetro isError. Esto permitirá actualizar la aparición del mismo en las recomposiciones
  4. Decidimos qué mensaje usaremos, si nameError es true, mostraremos el mensaje de error, de lo contrario el texto de ayuda
  5. El color también depende de nameError al igual que el texto, por lo que usamos los colores del tema error y onSurface
  6. Creamos al elemento de asistencia con los valores anteriores
  7. Realizamos la validación del nombre con la función de extensión isBlank() al interior del botón. Asignamos su resultado a nameError. Al cambiar su estado iniciaremos la recomposición para reflejar el nuevo estado en la interfaz

Mostrar Iconos

Los iconos en un campo de texto tienen diferentes objetivos como: indicar el tipo de contenido, indicar estado de error, proveer acciones de limpieza, disponibilidad de entrada por voz, etc.

Puedes añadir un icono líder al inicio y/o otro al final con los parámetros leadingIcon y trailingIcon. Ambos reciben una función componible que representa a su imagen, es decir, a componentes Icon o IconButton.

Ejemplo De leadingIcon

Añadir un icono de calendario al inicio de un campo de texto

@Composable
fun LeadingIconExample() {
    var createdDate by remember {
        mutableStateOf("")
    }

    TextField(
        value = createdDate,
        onValueChange = { createdDate = it },
        label = { Text("Fecha de inscripción") },
        placeholder = { Text("") },
        leadingIcon = {
            IconButton(onClick = { }) {
                Icon(
                    imageVector = Icons.Filled.DateRange,
                    contentDescription = "Botón para elegir fecha"
                )
            }
        }
    )
}
Ejemplo de leadingIcon en TextField Compose

Ejemplo trailingIcon

El icono de limpieza permite limpiar la entrada de un campo de texto. Este solo aparecerá solo cuando haya texto presente. Veamos cómo ubicar uno:

@Composable
fun TrailingIconExample() {
    var name by remember {
        mutableStateOf("Carlos")
    }

    TextField(
        value = name,
        onValueChange = { name = it },
        label = { Text("Nombre") },
        trailingIcon = {
            if (name.isNotBlank())
                IconButton(onClick = { name = "" }) {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_cancel),
                        contentDescription = "Limpiar campo de nombre"
                    )
                }
        }
    )
}
Ejemplo trailingIcon en TextField Compose

Como ves, solo si el valor de name no es blanco, mostramos el IconButton. En caso de ser mostrado, su controlador onClick asigna el literal vacío para cadenas a name.

Contador De Caracteres

El contador de caracteres se usa si deseas establecer un límite de caracteres en la entrada del campo de texto. Este representa la proporción entre los caracteres consumidos y el máximo.

Contador de caracteres TextField Compose

Al igual que los textos de ayuda y error, el contador no está en la preconstrucción de TextField, por lo que añadimos un componente Text en la parte inferior derecha.

@Composable
fun CharacterCounterExample() {
    var name by remember {
        mutableStateOf("")
    }
    val counterMaxLength = 20 //1

    Column {
        TextField(
            value = name,
            onValueChange = {
                if (it.length <= counterMaxLength) //2
                    name = it
            },
            label = { Text("Nombre") }
        )
        Text(
            text = "${name.length}/$counterMaxLength",//3
            color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
            style = MaterialTheme.typography.caption,
            modifier = Modifier
                .padding(start = 16.dp)
                .align(Alignment.End) //4
        )
    }
}

En la función componible anterior:

  1. Declaramos el tamaño máximo de caracteres para el campo de texto del nombre
  2. Desde onValueChange nos preguntamos «¿El tamaño del texto recibido es menor o igual que el límite permitido?». Si es así, entonces actualizamos al estado name
  3. Reflejamos la proporción de la cantidad de caracteres actual con respecto al límite en text
  4. Ya que el contador de caracteres va alineado a la derecha del campo de texto, usamos el modificador align() para conseguir esta posición

4. Transformaciones Visuales

Usa el parámetro visualTransformation para asignar una transformación visual al texto de entrada a fin de convertirlo a una presentación para el usuario.

Reemplazar Texto Con Asteriscos En Contraseñas

Compose tiene una transformación visual preconstruida llamada PasswordVisualTransformation para ocultar el texto de tus campos de texto con contraseñas. Este reemplaza en la escritura cada carácter tipeado por el símbolo *.

Ejemplo:

Recibir la contraseña del usuario en un campo de texto. Ocultar los caracteres de esta por defecto, pero permitirle revelarlos al presionar un botón de icono.

@Composable
fun PasswordExample() {
    var password by remember { mutableStateOf("") }
    var hidden by remember { mutableStateOf(true) } //1

    OutlinedTextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Contraseña") },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),//2
        singleLine = true,
        visualTransformation =
        if (hidden) PasswordVisualTransformation() else VisualTransformation.None,//3
        trailingIcon = {// 4
            IconButton(onClick = { hidden = !hidden }) {
                val vector = painterResource(//5
                    if (hidden) R.drawable.ic_visibility
                    else R.drawable.ic_visibility_off
                )
                val description = if (hidden) "Ocultar contraseña" else "Revelar contraseña" //6
                Icon(painter = vector, contentDescription = description)
            }
        }
    )
}
PasswordVisualTransformation en TextField Compose

En el código anterior:

  1. Declaramos al estado booleano hidden para alternar entre los vectores de visibilidad
  2. Usamos el tipo KeyboardType.Password para solicitar un teclado virtual para contraseñas
  3. Dependiendo del valor de hidden así mismo se aplica la transformación visual de PasswordVisualTransformation o None
  4. Añadimos un trailingIcon para el campo de texto
  5. Decidimos cuál será el vector según hidden
  6. Lo mismo hacemos para el texto de accesibilidad

Texto Como Prefijo/Sufijo

Los prefijos y sufijos de un campo de texto se ubican en el inicio y final del contenedor del campo de texto, con el fin de representar la naturaleza o contexto del contenido ingresado.

Para lograr esta característica, usa una transformación visual personalizada que acomode los textos en el inicio y final del contenedor. Lo que quiere decir que debes crear una clase que implemente a VisualTransformation.

Ejemplo De Prefijo

Añadir el símbolo del dólar como prefijo a un campo de texto que recibe el precio de un producto

@Composable
fun PrefixExample() {
    var price by remember {
        mutableStateOf("")
    }

    TextField(
        value = price,
        onValueChange = { price = it },
        label = { Text("Precio") },
        visualTransformation = PrefixVisualTransformation("$ "),
        keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
    )
}
Ejemplo prefijo TextField Compose

La clase PrefixVisualTransformation concatena el prefijo junto al texto ingresado por el usuario. Adicionalmente, se crea la clase PrefixOffsetMapping para convertir la posición del cursor del texto original en la del transformado y viceversa:

class PrefixVisualTransformation(private val prefix: String) : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        val transformedText = AnnotatedString(prefix, SpanStyle(Color.Gray)) + text

        return TransformedText(transformedText, PrefixOffsetMapping(prefix))
    }
}

class PrefixOffsetMapping(private val prefix: String) : OffsetMapping {
    override fun originalToTransformed(offset: Int): Int = offset + prefix.length

    override fun transformedToOriginal(offset: Int): Int {
        val delta = offset - prefix.length
        return if (delta < 0) 0 else delta
    }
}

Donde:

  • filter(): convierte la entrada del usuario en su nueva presentación
  • originalToTransformed(): Transforma el offset del texto original al del transformado
  • transformedToOriginal(): Traduce el offset del texto transforamdo al original

Ejemplo De Sufijo

Para mostrar un sufijo creamos una transformación visual y mapeador similar a las del prefijo. Lo único es cambiar el orden de la concatenación de los strings y la conversión del desplazamiento del cursor (offset).

class SuffixVisualTransformation(private val suffix: String) : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        val transformedText = text + AnnotatedString(suffix, SpanStyle(Color.Gray))

        return TransformedText(transformedText, SuffixOffsetMapping(text.text))
    }
}

class SuffixOffsetMapping(private val originalText: String) : OffsetMapping {
    override fun originalToTransformed(offset: Int): Int = offset

    override fun transformedToOriginal(offset: Int): Int {
        return if (offset > originalText.length) originalText.length else offset
    }
}

Por ejemplo:

Creemos un campo de texto que reciba las unidades en kilogramos de un peso y se muestre como sufijo el string " kg".

@Composable
fun SuffixExample() {
    var weight by remember {
        mutableStateOf("")
    }

    TextField(
        value = weight,
        onValueChange = { weight = it },
        label = { Text("Peso") },
        textStyle = TextStyle(textAlign = TextAlign.End),
        visualTransformation = SuffixVisualTransformation(" kg"),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
    )
}

5. Tipos De Entradas

TextField De Una Sola Línea

Cuando desees desplegar en una sola línea el texto ingresado por el usuario, pasa true al parámetro singleLine.

@Composable
fun SingleLineExample() {
    var description by remember {
        mutableStateOf("Campo de texto")
    }

    TextField(
        value = description,
        onValueChange = { description = it },
        label = { Text("Descripción") },
        singleLine = true,
        modifier = Modifier.width(200.dp)
    )
}

En el momento que el cursor alcanza el extremo derecho del contenedor, el contenido comenzará a desplazarse hacia la izquierda para dar paso a los nuevos caracteres.

Ejemplo singleLine TextField Compose

TextField De Múltiples Líneas

Un campo de texto con múltiples líneas comenzará con una sola línea, pero tiene la capacidad de expandirse a fin de mostrar todo el contenido que el usuario ingrese. Esto hace que los elementos que se encuentren por debajo sean desplazados para satisfacer la expansión.

Determina la cantidad de líneas que puede expandirse con el parámetro maxLines.

@Composable
fun SingleLineExample() {
    var description by remember {
        mutableStateOf("Campo de texto")
    }

    TextField(
        value = description,
        onValueChange = { description = it },
        label = { Text("Descripción") },
        maxLines = 3,
        modifier = Modifier.width(200.dp)
    )
}
Ejemplo de maxLines TextField Compose

Si singleLine está activo, el valor de maxLines será ignorado.

Ahora bien, maxLines con el valor de 1 consigue un efecto similar a singleLine, pero solo singleLine evita que el teclado ofrezca acción IME de salto de línea.

Áreas De Texto

Son campos de texto con un alto fijo y de mayor tamaño. Cuando se alcanza el límite del contenedor, el cursor hace un salto de línea y el contenido se desplaza hacia abajo.

Para representarlo aplicar algún modificar de altura para el campo de texto en su atributo modifier.

@Composable
fun TextAreaExample() {
    var description by remember {
        mutableStateOf("Área de texto con una altura fija de 150dp")
    }

    TextField(
        value = description,
        onValueChange = { description = it },
        label = { Text("Descripción") },
        modifier = Modifier
            .width(200.dp)
            .height(100.dp)
    )
}
Ejemplo de text area con TextField en Compose

TextFields De Solo Lectura

Los campos de texto de solo lectura vienen con un texto precargado que el usuario no puede editar. Sin embargo el usuario podrá copiar su texto o pasarle el foco.

Activa este comportamiento pasando true al parámetro readOnly de TextField.

Ejemplo:

@Composable
fun ReadOnlyExample() {
    OutlinedTextField(
        value = "INV-0001",
        onValueChange = { },
        label = { Text("Número de factura") },
        readOnly = true
    )
}
Ejemplo de readOnly en TextField Compose

6. Teclado Virtual

Opciones De Teclado

Las opciones de teclado, representadas por la clase KeyboardOptions, son el mecanismo para configurar el teclado para un TextField. No obstante, no se asegura que el teclado provea los ajustes solicitados.

Puedes pasar una instancia de estas opciones al parámetro keyboardOptions de los campos de texto. Su constructor es:

KeyboardOptions(
    capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
    autoCorrect: Boolean = true,
    keyboardType: KeyboardType = KeyboardType.Text,
    imeAction: ImeAction = ImeAction.Default
)

Donde:

  • capitalization: Indica si el teclado debe capitalizar los caracteres (Characters), palabras (Words) o sentencias (Sentences). Su valor por defecto es None para no capitalizar ninguna entrada
  • autoCorrect: Informa al teclado si debe activar la autocorreción
  • keyboardType: Determina el tipo de teclado usado para el campo de texto. Usa alguno de estos valores: Ascii, Email, Number, NumberPassword, Phone, Text o Uri
  • imeAction: Especifica el método de entrada (IME) para el teclado. Sus propiedades son: Default, Done, Go, Next, None, Previous, Search y Send

Por ejemplo:

Mostrar la acción Send en el teclado virtual para un campo de texto.

@Composable
fun KeyboardOptionsExample() {
    var category by remember {
        mutableStateOf("")
    }

    TextField(
        value = category,
        onValueChange = { category = it },
        label = { Text("Nombre de categoría") },
        keyboardOptions = KeyboardOptions.Default.copy(
            imeAction = ImeAction.Send
        )
    )
}
Ejemplo imeAction en TextField Compose
Acción IME Send en teclado virtual

El código anterior usa el método copy() para copiar las propiedades de la instancia Default con el fin de pasar como parámetros sólo los valores que alteramos. En este caso será imeAction, el cual recibe a ImeAction.Send.

Procesar Acciones De Teclado

Complementando, si deseas ejecutar acciones cuando el usuario ha disparado la acción del botón IME desde el teclado, entonces usa el parámetro keyboardActions.

Este recibe una instancia de la clase KeyboardActions, la cual posee el siguiente constructor:

KeyboardActions(
    onDone: KeyboardActionScope.() -> Unit = null,
    onGo: KeyboardActionScope.() -> Unit = null,
    onNext: KeyboardActionScope.() -> Unit = null,
    onPrevious: KeyboardActionScope.() -> Unit = null,
    onSearch: KeyboardActionScope.() -> Unit = null,
    onSend: KeyboardActionScope.() -> Unit = null
)

Como ves, son tipos función que podrás especificar para ejecutar las sentencias que desees según la acción IME.

Ejemplo:

Procesar la acción IME del ejemplo anterior para guardar el nombre de la categoría y mostrarla en pantalla

@Composable
fun KeyboardActionsExample() {
    var category by remember {
        mutableStateOf("")
    }

    var categories by remember {
        mutableStateOf("Categorías:\n")
    }

    Column {

        TextField(
            value = category,
            onValueChange = { category = it },
            label = { Text("Nombre de categoría") },
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Send
            ),
            keyboardActions = KeyboardActions(onSend = {
                categories += "- $category\n"
            })
        )
        Spacer(Modifier.size(16.dp))
        Text(categories)
    }
}

En este ejemplo solo usamos el parámetro onSend, pasandole una lambda que actualiza el estado de un Text que muestra la concatenación de las categorías que van siendo guardadas.

En el caso de que quieras procesar cualquier acción en un solo lugar, usa la función KeyboardActions(), la cual recibe al parámetro onAny que será aplicada a todas las acciones:

keyboardActions = KeyboardActions(onAny = {
    categories += "- $category\n"
})

Ocultar Teclado

La propiedad de composición local que se asocian con el cierre del teclado es la interfaz SoftwareKeyboardController. Puedes obtener una implementación a través de LocalSoftwareKeyboardController y usar sus métodos hide()/show() para ocultar/mostrar el teclado.

Ejemplo:

Ocultar teclado luego de que se realiza la acción IME del ejemplo anterior.

@ExperimentalComposeUiApi
@Composable
fun KeyboardActionsExample() {
    //...

    val keyboardController = LocalSoftwareKeyboardController.current 

    Column {

        TextField(
            //...
            keyboardActions = KeyboardActions(onSend = {
                keyboardController?.hide() 
                //...
            })
        )
        //...
    }
}

En primer lugar obtenemos el controlador en la variable keyboardController y luego cuando se ejecute la acción invocamos su método hide().


7. Aplicar Estilos A TextField

Recuerda que el TextField hace parte de la categoría de componentes pequeños comprendidos en el sistema de Material Design, por lo que el tema que apliques definirá su figura, fuente tipográfica y colores usados.

Pero si deseas cambiar estos aspectos en un solo campo de texto, entonces usa los parámetros:

  • textStyle: Define el estilo de un texto con una instancia de la clase TextStyle
  • colors: Aplica los colores del campo de texto con una instancia del tipo TextFieldColors. Invoca a la función componible textFieldColors() para definir el color de cada decoración del campo de texto
  • shape: La forma del campo de texto determinada por un tipo Shape

Ejemplo:

Personalizar la familia tipográfica del texto de entrada, los colores y forma de un campo de texto.

@Composable
fun TextFieldStyleExample() {
    var nameOnCard by remember {
        mutableStateOf("Américo Vespucio")
    }

    val fontFamily = FontFamily(Font(R.font.playfairdisplay_regular))
    val color = Color(0xFF120524)
    val shape = CutCornerShape(ZeroCornerSize)

    TextField(
        value = nameOnCard,
        onValueChange = { nameOnCard = it },
        label = { Text("Nombre en la tarjeta") },
        textStyle = TextStyle(fontFamily = fontFamily),
        colors = TextFieldDefaults.textFieldColors(
            textColor = color,
            backgroundColor = Color.White,
            focusedLabelColor = color.copy(alpha = ContentAlpha.high),
            focusedIndicatorColor = Color.Transparent,
            cursorColor = color,
        ),
        shape = shape,
        modifier = Modifier.border(1.dp, color)
    )
}

Como ves, fontFamily aplica la fuente Playfair Display, color usa un vinotinto oscuro y shape crea una elemento CutCornerShape sin afección en sus esquinas.

La función textFieldColors() permite asignar al parámetro colors el color para todos los elementos del TextField. En este caso usamos:

  • textColor: El color del texto de entrada
  • backgroundColor: El color de fondo del contenedor del campo de texto
  • focusedLabelColor: Color de la etiqueta cuando el campo tiene el foco
  • focusedIndicatorColor: Color para el indicador que recubre al campo de texto
  • cursorColor: Es el color del cursor que titila al escribir

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!