How to Implement Radio Button in Jetpack Compose?

jetpack compose radio button

In this article, we’ll 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

For this article, create an empty Jetpack Compose project and open MainActivity. Add a composable called MyUI() and call it from the onCreate() method.

// add the following packages
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme(darkTheme = false) {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {
   
}

We will write our code in the MyUI().

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 for changing the layout.

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

interactionSourceThe MutableInteractionSource for observing the interactions.

colorsThe 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

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)
            }
        }
    }
}

Output:

Custom Indicator

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