Switch En Compose

Este tutorial te enseña sobre el uso del componente Switch en Compose en Android a través de varios ejemplos fáciles de comprender.

El Switch es un control de selección que te permite elegir entre dos posibles estados. Cambia entre sus valores arrastrando la palanca redondeada (thumb), sobre el carril (track) en el que se encuentra. O simplemente haz click en el componente para alternar entre estados.


Ejemplos De Switch En Compose

El código de los ejemplos de este tutorial los encontrarás en el paquete examples/Switch del paquete p7_componentes.

Los ejemplos son desplegados (como se muestra en la siguiente imagen) desde la función SwitchScreen() en SwitchScreen.kt:


1. Crear Un Switch

Switch Simple en Compose
Figura 1. Switch Simple en Compose

Invoca la función Switch() para construir y mostrar en pantalla un Switch. La cual está definida con la siguiente firma en la documentación oficial:

@Composable
fun Switch(
    checked: Boolean?,
    onCheckedChange: ((Boolean) -> Unit)?,
    modifier: Modifier? = Modifier,
    enabled: Boolean? = true,
    interactionSource: MutableInteractionSource? = remember { MutableInteractionSource() },
    colors: SwitchColors? = SwitchDefaults.colors()
): Unit

Sus parámetros cumplen con:

  • checked: Determina si el switch se encuentra en estado On u Off
  • onCheckedChange: Función invocada cuando el switch ha sido clickeado
  • enabled: Determina si el switch está habilitado o deshabilitado
  • colors: Instancia de SwitchColors para representar los colores del thumb y el track en diferentes estados

Ejemplo: Un Switch Simple

Para crear un Switch en su forma básica, como ves en la figura 1, implementa el siguiente código Kotlin:

@Composable
fun SimpleSwitch() {
    val isOn = remember { mutableStateOf(true) }

    Switch(
        checked = isOn.value,
        onCheckedChange = { newState -> isOn.value = newState }
    )
}

Su estado inicial es On, ya que la variable de estado isOn es inicializada con true. Adicionalmente, alternamos entre estados, al modificar el valor de isOn desde una lambda asignada a onCheckedChange.

Switch simple en acción
Resultado 1. Switch simple en acción

Si ejecutas la App de ejemplo o previsualizas la función Example1() en modo de interacción, experimentarás la secuencia de Resultado 1.

@Composable
fun Example1() {
    SimpleSwitch()
}

Nota: Esta implementación es sumamente similar a la del control RadioButton.


2. Añadir Etiqueta A Un Switch

Switch con etiqueta
Figura 2. Switch con etiqueta

Para aclarar la opción que el switch controla y el estado actual de la misma, es necesario incorporar una etiqueta de texto como se ve en la Figura 2.

A diferencia del SwitchView en el sistema de views, cuyo atributo android:text permite definir la etiqueta, la función Switch() en Compose no la trae como parámetro.

Ejemplo: Switch Con Etiqueta

Tomemos como ejemplo el registro de un usuario en un servicio, donde se le desea ofrecer el envío de ofertas especiales a su correo electrónico.

Con el fin de llegar a la solución, (similar al CheckBox y el RadioButton) es necesario crear un layout que envuelva tanto la etiqueta, como el control Switch.

Las siguientes son las tareas para llevarlo a cabo:

  1. Crear una función componible que eleve el estado del switch y reciba la etiqueta como parámetro
  2. Invocar a la función Box()
  3. Incluir un elemento Text() alineado al centro|inicio y luego uno Switch() alineado al centro|final
  4. Trasladar el cambio de estado desde el Switch hacia la fila con el modificador toggeable()

Atendiendo los lineamientos anteriores, la nueva función componible para representar un switch con etiqueta será:

@Composable
fun LabelledSwitch( // (1)
    modifier: Modifier = Modifier,
    checked: Boolean,
    label: String,
    onCheckedChange: ((Boolean) -> Unit),
    enabled: Boolean = true,
    colors: SwitchColors = SwitchDefaults.colors()
) {

    Box( // (2)
        modifier = modifier
            .fillMaxWidth()
            .height(56.dp)
            .toggleable( // (4)
                value = checked,
                onValueChange = onCheckedChange,
                role = Role.Switch,
                enabled = enabled
            )
            .padding(horizontal = 16.dp)

    ) {
        CompositionLocalProvider(
            LocalContentAlpha provides
                    if (enabled) ContentAlpha.high else ContentAlpha.disabled
        ) {
            Text( // (3)
                text = label,
                style = MaterialTheme.typography.body1,
                modifier = Modifier
                    .align(Alignment.CenterStart)
                    .padding(end = 16.dp)
            )
        }

        Switch( // (3)
            checked = checked,
            onCheckedChange = null, // (4)
            enabled = enabled,
            colors = colors,
            modifier = Modifier.align(Alignment.CenterEnd)
        )
    }
}

Cabe aclarar que cuando se procesa el estado deshabilitado, el texto debe cambiar su énfasis a ContentAlpha.disabled. Esto mejora la expresividad del conjunto.

Al final, invoca la anterior función de la siguiente manera:

@Composable
fun Example2() {
    val offersReceiving = "Recibir ofertas especiales"
    val offersReceivingState = remember { mutableStateOf(true) }

    LabelledSwitch(
        checked = offersReceivingState.value,
        label = offersReceiving,
        onCheckedChange = { offersReceivingState.value = it }
    )
}

Al ejecutar o entrar a modo interactivo podrás ver el siguiente resultado:

Modificador toggeable() Compose
Resultado 2. Switch con etiqueta y modificador toggeable()

3. Deshabilitar Un Switch

Figura 3. Switch activo en estado habilitado y deshabilitado

Deshabilitar un switch es intuitivo, basta con pasar false como argumento al parámetro enabled de Switch(). Esto provocará que el control se torne de un tono grisáceo y no reciba eventos de entrada del usuario como se ven en la figura 3.

Ejemplo: Switch Deshabilitado

Switch para habilitar informes semanales
Figura 4. Switch para habilitar informes semanales

Supongamos que los usuarios de una App pueden activar la notificación de reportes semanales. No obstante, esta característica es solo para usuarios premium, por lo que debe deshabilitarse en caso contrario.

La solución consiste en:

  1. Recibir los ajustes del usuario que la App almacena
  2. Verificar que la propiedad de su tipo de plan sea correcto
  3. Crear un switch con etiqueta y pasar la evaluación anterior a enabled

En código Kotlin esto significa:

@Composable
fun Example3() {
    val (isPremium, setIsPremium) = remember { mutableStateOf(false) }
    val (notifyWeekly, setNotifyWeekly) = remember { mutableStateOf(false) }

    Column(Modifier.padding(vertical = 16.dp)) {

        LabelledCheckbox(
            checked = isPremium,
            onCheckedChange = setIsPremium,
            label = "¿Usuario premium?"
        )
        
        Spacer(modifier = Modifier.height(56.dp))
        
        LabelledSwitch(
            checked = notifyWeekly,
            label = "Habilitar informes semanales",
            onCheckedChange = setNotifyWeekly,
            enabled = isPremium
        )
    }
}

La utilidad del CheckBox agregado es simular los dos escenarios posibles para el plan del usuario, de esta forma veremos la desactivación del Switch.

Resultado 3. Deshabilitar switch de informes semanales

Por supuesto, el valor de isPremium debería ser obtenido desde una clase relacionada a la obtención de datos en nuestra App (comúnmente un ViewModel comunicado a un repositorio, servicio, dao, etc.).


4. Cambiar Color De Un Switch

Anatomía de un Switch
Figura 5. Anatomía de un Switch

Por último, veamos el caso en que requieras modificar el color de un Switch en sus diferentes estados. Con esto me refiero a cambiar el color de los elementos thumb y track.

Cómo viste al inicio, el atributo colors recibe una instancia de SwitchColors para este cometido. La forma de fabricarla es con la función SwitchDefaults.colors():

@Composable
fun colors(
    checkedThumbColor: Color? = MaterialTheme.colors.secondaryVariant,
    checkedTrackColor: Color? = checkedThumbColor,
    checkedTrackAlpha: Float? = 0.54f,
    uncheckedThumbColor: Color? = MaterialTheme.colors.surface,
    uncheckedTrackColor: Color? = MaterialTheme.colors.onSurface,
    uncheckedTrackAlpha: Float? = 0.38f,
    disabledCheckedThumbColor: Color? = checkedThumbColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface),
    disabledCheckedTrackColor: Color? = checkedTrackColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface),
    disabledUncheckedThumbColor: Color? = uncheckedThumbColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface),
    disabledUncheckedTrackColor: Color? = uncheckedTrackColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface)
): SwitchColors

La configuración de parámetros nombrados obedece a los valores checked (On) y unchecked (Off) y al estado disabled (deshabilitado). Sumado al componente que se modifica: thumb o track.

Ejemplo: Switch Con Color Modificado

Basemonos en el ejemplo 3 para modificar el color de la pista y la palanca por los siguientes:

  • Palanca -> On + Habilitado = Azul 500
  • Pista -> On + Habilitado = Azul Claro 500
  • Pista -> Off + Habilitado = Azul Claro 300

Con lo anterior en mente, solo resta definir los colores y aplicarlos con la función SwitchDefaults.colors():

val Blue500 = Color(0xFF2196F3)
val LightBlue500 = Color(0xFF00BCD4)
val LightBlue300 = Color(0xFF4DD0E1)

@Composable
fun Example4() {
    val (isPremium, setIsPremium) = remember { mutableStateOf(false) }
    val (notifyWeekly, setNotifyWeekly) = remember { mutableStateOf(false) }

    Column(Modifier.padding(vertical = 16.dp)) {

        LabelledCheckbox(
            checked = isPremium,
            onCheckedChange = setIsPremium,
            label = "¿Usuario premium?"
        )

        Spacer(modifier = Modifier.height(56.dp))

        LabelledSwitch(
            checked = notifyWeekly,
            label = "Habilitar informes semanales",
            onCheckedChange = setNotifyWeekly,
            enabled = isPremium,
            colors = SwitchDefaults.colors(
                checkedThumbColor = Blue500,
                uncheckedThumbColor = LightBlue500,
                uncheckedTrackColor = LightBlue300
            )
        )
    }
}

El resultado final luciría así:

Cambio de color de track y thumb de un Switch
Resultado 4. Cambio de color de track y thumb de un Switch

Ú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!