简体   繁体   中英

Jetpack Compose Material3 - Label for Switch

I need to implement a switch with a label with Jetpack Compose and Material3.

My solution so far (it basically just extends the existing switch component and adds the label property):

@Composable
fun LabeledSwitch(
    checked: Boolean,
    onCheckedChange: ((Boolean) -> Unit)?,
    modifier: Modifier = Modifier,
    thumbContent: (@Composable () -> Unit)? = null,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: SwitchColors = SwitchDefaults.colors(),
    label: (@Composable () -> Unit),
) {
    Row(
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        label()
        Switch(
            checked = checked,
            onCheckedChange = onCheckedChange,
            thumbContent = thumbContent,
            enabled = enabled,
            interactionSource = interactionSource,
            colors = colors
        )
    }
}

This correctly displays the label (eg {Text("Test")} ) next to the switch.

However, I would like to forward all click events on the label to the switch, so that you can touch the label to toggle the switch value.

It basically should work like the old <Switch> component in XML layouts.

One idea I had was adding a modifier to the container like so:

Row(
    horizontalArrangement = Arrangement.SpaceBetween,
    verticalAlignment = Alignment.CenterVertically,
    modifier = modifier.then(Modifier
        .clickable { onCheckedChange?.invoke(!checked) }
    )

But this is not optimal as it shows a ripple effect on the whole item.

Is there any better solution? Maybe even without a custom component?

You can do it like this. If you don't want ripple on Switch set onCheckedChange = null

在此处输入图像描述

@Composable
private fun SwitchWithLabel(label: String, state: Boolean, onStateChange: (Boolean) -> Unit) {

    val interactionSource = remember { MutableInteractionSource() }
    Row(
        modifier = Modifier
            .clickable(
                interactionSource = interactionSource,
                // This is for removing ripple when Row is clicked
                indication = null,
                role = Role.Switch,
                onClick = {
                    onStateChange(!state)
                }
            )
            .padding(8.dp),
        verticalAlignment = Alignment.CenterVertically

    ) {

        Text(text = label)
        Spacer(modifier = Modifier.padding(start = 8.dp))
        Switch(
            checked = state,
            onCheckedChange = {
                onStateChange(it)
            }
        )
    }
}

You can use the wrapContentSize modifier in your Row to avoid the ripple in the whole width.

var isChecked by remember { mutableStateOf(false) }

Row(
    modifier = Modifier
        .wrapContentSize()
        .toggleable(
            value = isChecked,
            onValueChange = { isChecked = it },
        ),
) {
    Text(text = "Hello")
    Switch(
       checked = isChecked, 
       onCheckedChange = null,
       modifier = Modifier.semantics { contentDescription = "Hello" }
    )
}

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM