import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.composeunstyled.RadioButton
import com.composeunstyled.SelectedIndicator
import com.composeunstyled.UnstyledRadioGroup
@Composable
fun RadioGroupDemo() {
val values = listOf("Light", "Dark", "System")
var selectedValue by remember { mutableStateOf("Light") }
Column(
modifier = Modifier
.width(300.dp)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
UnstyledRadioGroup(
value = selectedValue,
onValueChange = { selectedValue = it },
modifier = Modifier.fillMaxWidth(),
accessibilityLabel = "Theme selection",
) {
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth(),
) {
values.forEach { value ->
val selected = selectedValue == value
val itemShape = RoundedCornerShape(14.dp)
RadioButton(
value = value,
modifier = Modifier
.fillMaxWidth()
.clip(itemShape),
indication = LocalIndication.current,
) {
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.size(20.dp)
.clip(CircleShape)
.background(
if (selected) Color.Black else Color(0xFFF8FAFC),
)
.border(1.dp, Color(0xFFCACACA), CircleShape),
contentAlignment = Alignment.Center,
) {
SelectedIndicator(
indication = LocalIndication.current,
) {
Box(
Modifier
.size(8.dp)
.clip(CircleShape)
.background(Color.White),
)
}
}
Spacer(Modifier.width(16.dp))
BasicText(value)
}
}
}
}
}
}
}Features
- Generic radio values
- Arrow-key focus movement
- Animated selected indicator
Installation
implementation("com.composables:composeunstyled-radio-group")
Anatomy
UnstyledRadioGroup(
value = value,
onValueChange = onValueChange,
) {
RadioButton(value) {
SelectedIndicator {
}
}
}
Concepts
UnstyledRadioGroupgroups radio options for one selected value.RadioButtonrenders an option inside the group.SelectedIndicatorrenders only when its radio button is selected.
Accessibility
Use accessibilityLabel when the radio group does not contain a readable group label.
Code Examples
Selecting one radio option
Use the value parameter on each RadioButton to connect it to the group value:
var selected by remember { mutableStateOf("small") }
UnstyledRadioGroup(
value = selected,
onValueChange = { selected = it },
) {
RadioButton("small") {
BasicText("Small")
}
RadioButton("large") {
BasicText("Large")
}
}
Rendering the selected indicator
Use the SelectedIndicator component to render content only for the selected option:
RadioButton("large") {
SelectedIndicator {
BasicText("Selected")
}
BasicText("Large")
}
Animating the selected indicator
Use the enter and exit parameters on SelectedIndicator to animate the selected indicator:
RadioButton("large") {
SelectedIndicator(
enter = fadeIn(),
exit = fadeOut(),
) {
BasicText("Selected")
}
}
API Reference
UnstyledRadioGroup
| Parameter | Type | Description |
|---|---|---|
value |
T? |
The value associated with the radio button. |
onValueChange |
(T) -> Unit |
|
modifier |
Modifier |
Modifier to be applied to the radio button. |
accessibilityLabel |
String? |
|
content |
RadioGroupScope.() -> Unit |
Composable function to define the content of the radio button. |
RadioGroupScope.RadioButton
| Parameter | Type | Description |
|---|---|---|
value |
T |
The value associated with the radio button. |
modifier |
Modifier |
Modifier to be applied to the radio button. |
enabled |
Boolean |
Whether the radio button is enabled. |
interactionSource |
MutableInteractionSource? |
Interaction source for the radio button. |
indication |
Indication? |
Visual indication for interactions. |
content |
RadioButtonScope.() -> Unit |
Composable function to define the content of the radio button. |
RadioButtonScope.SelectedIndicator
| Parameter | Type | Description |
|---|---|---|
modifier |
Modifier |
Modifier to be applied to the radio button. |
indication |
Indication? |
Visual indication for interactions. |
enter |
EnterTransition |
|
exit |
ExitTransition |
|
content |
AnimatedVisibilityScope.() -> Unit |
Composable function to define the content of the radio button. |