Create Toggle Button Group in Jetpack Compose without Radio Buttons
Asked Answered
S

2

8

I am trying to put a button toggle group in my project that behaves similarly to a radio button group, but does not look like a radio button group (ie when one button is selected, the others are deselected).

I followed a radio button pattern I found online, but that doesn't seem to be doing the trick. Is there a way to do this? I've gotten to a point where I have the buttons in the place I want them, but they're both disabled.

MovieSpotterTheme() {
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                ) {
                    @Composable
                    fun MaterialButtonToggleGroup() {
                        var selected by remember { mutableStateOf("Android") }

                        val buttonGroup = listOf("Popular Movies", "Search Movies")

                     
                        val onSelectedChange = { text: String ->
                            selected = text
                        }
                        Row(
                            horizontalArrangement = Arrangement.SpaceEvenly
                        ) {
                            buttonGroup.forEach { text ->
                                Row(Modifier
                                    .selectable(
                                        selected = (text == selected),
                                        onClick = { onSelectedChange(text) }
                                    )
                                    .padding(horizontal = 16.dp)
                                ) {
                                    Button(
                                        enabled = (text == selected),
                                        onClick = { onSelectedChange(text) }
                                    ) {
                                        Column(
                                            horizontalAlignment = Alignment.CenterHorizontally
                                        ) {
                                            Text(
                                                text = text,
                                                style = MaterialTheme.typography.body1.merge(),
                                                modifier = Modifier.padding(horizontal = 16.dp)
                                            )
                                        }
                                    }
                                }
                            }
                        }
                    }
                    Surface() {
                        MaterialButtonToggleGroup()
                    }
                }
            }
Spheroid answered 31/10, 2021 at 16:13 Comment(6)
Remove enabled = (text == selected) from Button. Value for enabled parameter evaluates to false initially since selected = "Android" which does not equals "Popular Movies" or "Search Movies". This makes both buttons have state disabled.Idiolect
Why there is nested @Composable fun in this code?Spiegeleisen
It's setup a little differently. This is actually inside of a fragment that displays as part of an xml file (part of an assignment for a job interview). Admittedly, I may be doing it wrong. I'm sort of new.Spheroid
@OmKumar Right. I got that much. I just don't know how to make them toggle back and forth. This has the opposite solution where they're both enabled at all times.Spheroid
Not sure why you want to disable selected button. Reselecting a selected button won't harm anyways. Material spec doesn't require disabling it.Idiolect
Yeah, I was trying to disable the button that was selected to change the appearance and make it unselectable to illustrate that it was the one currently selected.Spheroid
S
20

Providing a simplified version. Play around with it to suit your requirement.

@Composable
fun CustomRadioGroup() {
    val options = listOf(
        "Option 1",
        "Option 2",
        "Option 3",
        "Option 4",
    )
    var selectedOption by remember {
        mutableStateOf("")
    }
    val onSelectionChange = { text: String ->
        selectedOption = text
    }

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.fillMaxSize(),
    ) {
        options.forEach { text ->
            Row(
                modifier = Modifier
                    .padding(
                        all = 8.dp,
                    ),
            ) {
                Text(
                    text = text,
                    style = typography.body1.merge(),
                    color = Color.White,
                    modifier = Modifier
                        .clip(
                            shape = RoundedCornerShape(
                                size = 12.dp,
                            ),
                        )
                        .clickable {
                            onSelectionChange(text)
                        }
                        .background(
                            if (text == selectedOption) {
                                Color.Magenta
                            } else {
                                Color.LightGray
                            }
                        )
                        .padding(
                            vertical = 12.dp,
                            horizontal = 16.dp,
                        ),
                )
            }
        }
    }
}

Radio Group

Spiegeleisen answered 31/10, 2021 at 16:43 Comment(1)
This was perfect! I rearranged some things and it works great! Thank you so much!Spheroid
K
0

In my case I had to do it like this

@Composable
fun ToggleList() {
    val musicList = listOf(
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
        Musics("Test song "),
    )

    val playing: SnapshotStateList<Boolean> = mutableStateListOf()

    musicList.onEachIndexed { index, _ ->
        playing.add(index, false)
    }

    val onSelectionChange = { index: Int, playerState: Boolean ->
        musicList.onEachIndexed { i, _ ->
            playing[i] = false
        }
        playing[index] = playerState
    }

    LazyColumn(
        content = {
            itemsIndexed(items = musicList) { index, item ->
                Card(
                    shape = RoundedCornerShape(10.dp),
                    elevation = CardDefaults.cardElevation(4.dp),
                    colors = CardDefaults.cardColors(MaterialTheme.colorScheme.background),
                    border = BorderStroke(4.dp, MaterialTheme.colorScheme.secondaryContainer),
                ) {
                    Text(
                        text = item.musicName.plus(index),
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(16.dp)
                    )
                    Icon(
                        modifier = Modifier
                            .clickable {
                                if (playing[index].not()) {
                                    onSelectionChange(index, true)
                                } else {
                                    onSelectionChange(index, false)
                                }
                            }
                            .size(70.dp)
                            .padding(10.dp)
                            .align(Alignment.End),
                        painter = if (playing[index]) {
                            painterResource(id = android.R.drawable.ic_media_pause)
                        } else {
                            painterResource(id = android.R.drawable.ic_media_play)
                        },
                        contentDescription = "play|pause songs",
                        tint = MaterialTheme.colorScheme.onBackground
                    )
                }
            }
        }
    )
}

data class Musics(
    val musicName: String = ""
)

enter image description here

Kliman answered 7/9, 2023 at 23:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.