
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:

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.
modifier – The 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.
interactionSource – The MutableInteractionSource for observing the interactions.
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:

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:

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:

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:

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:

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:

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: