How to Implement Radio Button in Jetpack Compose?

jetpack compose radio button

In this article, we will learn how to implement the radio button and radio group in Jetpack Compose.

Prerequisites:

What is the Radio Button in Android?

Radio buttons allow users to select a single option from a list.

Example:

Radio Buttons Group

Jetpack Compose provides RadioButton API:

@Composable
fun RadioButton(
    selected: Boolean,
    onClick: (() -> Unit)?,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: RadioButtonColors = RadioButtonDefaults.colors()
)

selected – Whether this radio button is selected or not.

onClick – It is invoked when the RadioButton is clicked. If null, this RadioButton will not handle input events.

modifierThe modifier.

enabled – Whether the radio button is enabled. If it is false, the button will not be selectable and appears disabled.

interactionSourceThe MutableInteractionSource.

colors – The colors to be applied to the button.

If you look at it, there is no parameter for label or text. So, if we implement it, we will only see a circle.

@Composable
fun MyUI() {

    RadioButton(
        selected = true,
        onClick = { }
    )
}

Output:

Radio Button

To display the label, we can use the Row layout with Text().

@Composable
fun MyUI() {
    Row(verticalAlignment = Alignment.CenterVertically) {
        RadioButton(
            selected = true,
            onClick = { }
        )
        Text(text = "Label")
    }
}

Output:

Radio Button Text Label

Jetpack Compose Radio Group:

There is no RadioGroup API in Jetpack Compose. We can use the Modifier.selectableGroup() to achieve similar functionality.

@Composable
fun MyUI() {
    val radioOptions = listOf("Mangoes", "Apple", "Melons")

    var selectedItem by remember {
        mutableStateOf(radioOptions[0])
    }

    Column(modifier = Modifier.selectableGroup()) {

        radioOptions.forEach { label ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(56.dp)
                    .selectable(
                        selected = (selectedItem == label),
                        onClick = { selectedItem = label },
                        role = Role.RadioButton
                    )
                    .padding(horizontal = 16.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                RadioButton(
                    modifier = Modifier.padding(end = 16.dp),
                    selected = (selectedItem == label),
                    onClick = null // null recommended for accessibility with screen readers
                )
                Text(text = label)
            }
        }
    }
}

Output:

Radio Buttons Group

In the code, we have made the Column layout a selectable group. We added Modifier.selectable on each Row. It makes the Row clickable. This is how selectable() looks:

fun Modifier.selectable(
    selected: Boolean,
    enabled: Boolean = true,
    role: Role? = null,
    onClick: () -> Unit
)

selected – Whether the item is selected.

enabled – Whether the item is enabled. If it is false, the item will not be clickable.

onClick – It is called when the item is clicked.

role – The type of user interface element. Available options are Button, Checkbox, Switch, RadioButton, Tab, and Image.

Radio Button Colors:

We can customize the radio button colors using the RadioButtonDefaults object. It has colors() method that returns the color based on the state of the button.

@Composable
fun colors(
    selectedColor: Color = MaterialTheme.colors.secondary,
    unselectedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
    disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
)

Let’s create radio buttons with the following colors:

selected item – Magenta
unselected item – DarkGray
disabled item – LightGray

@Composable
fun MyUI() {
    val radioOptions = listOf("Credit Card", "PayPal", "Debit Card (not available)")

    var selectedItem by remember {
        mutableStateOf(radioOptions[0])
    }

    Column(modifier = Modifier.selectableGroup()) {

        radioOptions.forEach { label ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(56.dp)
                    .selectable(
                        selected = (selectedItem == label),
                        onClick = { selectedItem = label },
                        role = Role.RadioButton,
                        // disable the debit card
                        enabled = (label != radioOptions[2])
                    )
                    .padding(horizontal = 16.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                RadioButton(
                    modifier = Modifier.padding(end = 16.dp),
                    selected = (selectedItem == label),
                    colors = RadioButtonDefaults.colors(
                        selectedColor = Color.Magenta,
                        unselectedColor = Color.DarkGray,
                        disabledColor = Color.LightGray
                    ),
                    // disable the debit card
                    enabled = (label != radioOptions[2]),
                    onClick = null
                )
                Text(
                    text = label,
                    color = if (label != radioOptions[2]) Color.DarkGray else Color.LightGray
                )
            }
        }
    }
}

Output:

Custom colors

Radio Button Custom Indicator:

We cannot customize the RadioButton, but we can replace it with other composables like Icon.

For example, let’s make this list:

Custom Indicator

Replace the RadioButton with the Icon like this:

Icon(
    modifier = Modifier.padding(end = 16.dp),
    imageVector = if (selectedItem == label)
        Icons.Outlined.CheckCircle else
        Icons.Outlined.RadioButtonUnchecked,
    contentDescription = null,
    tint = Color.Magenta
)

Here is the complete code:

To import the icons, add material icons dependency.

@Composable
fun MyUI() {
    val radioOptions = listOf("Linux", "Mac", "Windows")

    var selectedItem by remember {
        mutableStateOf(radioOptions[0])
    }

    Column(modifier = Modifier.selectableGroup()) {

        radioOptions.forEach { label ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(56.dp)
                    .selectable(
                        selected = (selectedItem == label),
                        onClick = { selectedItem = label },
                        role = Role.RadioButton
                    )
                    .padding(horizontal = 16.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Icon(
                    modifier = Modifier.padding(end = 16.dp),
                    imageVector = if (selectedItem == label)
                        Icons.Outlined.CheckCircle else
                        Icons.Outlined.RadioButtonUnchecked,
                    // screen readers will read the Text() compsable content
                    // if we pass label here, they end up reading the content twice
                    // so, pass null
                    contentDescription = null,
                    tint = Color.Magenta
                )
                Text(text = label)
            }
        }
    }
}

This is all about the radio button in Jetpack Compose. I hope you have learned something new. If you have any doubts, comment below.

Continue Exploring Jetpack Compose:

References:

Leave a Comment