Accordion provides expandable/collapsible content panels. It supports animations, grouped behavior, and flexible state management.
Parameters
modifier
Modifier
default:"Modifier"
Modifier to be applied to the accordion container
Modifier to be applied to the header section
state
AccordionState
default:"rememberAccordionState()"
State object that controls expansion and animation
Whether to animate the expansion/collapse
interactionSource
MutableInteractionSource
default:"MutableInteractionSource()"
Source of interactions for the accordion
Content displayed in the header section (always visible)
bodyContent
@Composable () -> Unit
required
Content displayed when the accordion is expanded
AccordionState
State object for controlling accordion behavior:
Whether the accordion can be toggled
Whether the accordion responds to clicks
onExpandedChange
((Boolean) -> Unit)?
default:"null"
Callback invoked when expansion state changes
AccordionState Methods
toggle(): Toggle between expanded and collapsed
collapse(): Collapse the accordion
updateProgress(Float): Update animation progress
AccordionState Properties
expanded: Boolean: Current expansion state
animationProgress: Float: Current animation progress (0f to 1f)
Usage Examples
Basic Accordion
Accordion(
headerContent = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Click to expand")
Icon(
imageVector = Icons.Default.ExpandMore,
contentDescription = null
)
}
},
bodyContent = {
Text(
text = "This is the expanded content",
modifier = Modifier.padding(16.dp)
)
}
)
Accordion with State Control
val accordionState = rememberAccordionState(
expanded = false,
onExpandedChange = { isExpanded ->
println("Accordion is now: ${if (isExpanded) "expanded" else "collapsed"}")
}
)
Accordion(
state = accordionState,
headerContent = {
Text("Controlled Accordion")
},
bodyContent = {
Text("Body content")
}
)
// Control externally
Button(onClick = { accordionState.toggle() }) {
Text("Toggle Accordion")
}
Accordion with Rotation Animation
val state = rememberAccordionState()
Accordion(
state = state,
headerContent = {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Animated Header")
Icon(
imageVector = Icons.Default.ExpandMore,
contentDescription = null,
modifier = Modifier.rotate(state.animationProgress * 180f)
)
}
},
bodyContent = {
Column(modifier = Modifier.padding(16.dp)) {
Text("Line 1")
Text("Line 2")
Text("Line 3")
}
}
)
Accordion Without Animation
Accordion(
animate = false,
headerContent = {
Text("Instant expand/collapse")
},
bodyContent = {
Text("Body content appears instantly")
}
)
Disabled Accordion
val state = rememberAccordionState(
expanded = false,
enabled = false
)
Accordion(
state = state,
headerContent = {
Text(
text = "Disabled Accordion",
color = AppTheme.colors.onDisabled
)
},
bodyContent = {
Text("This content cannot be accessed")
}
)
Accordion Groups
For managing multiple accordions where only one can be open at a time:
AccordionGroupState
Number of accordions in the group
Whether multiple accordions can be open simultaneously
Single-Open Group
val groupState = rememberAccordionGroupState(
count = 3,
allowMultipleOpen = false
)
Column {
Accordion(
state = groupState.getState(0),
headerContent = { Text("Accordion 1") },
bodyContent = { Text("Content 1") }
)
HorizontalDivider()
Accordion(
state = groupState.getState(1),
headerContent = { Text("Accordion 2") },
bodyContent = { Text("Content 2") }
)
HorizontalDivider()
Accordion(
state = groupState.getState(2),
headerContent = { Text("Accordion 3") },
bodyContent = { Text("Content 3") }
)
}
Multiple-Open Group
val groupState = rememberAccordionGroupState(
count = 3,
allowMultipleOpen = true
)
// Multiple accordions can be open at the same time
Column {
repeat(3) { index ->
Accordion(
state = groupState.getState(index),
headerContent = { Text("Item $index") },
bodyContent = { Text("Content $index") }
)
}
}
// Collapse all
Button(onClick = { groupState.collapseAll() }) {
Text("Collapse All")
}
// Expand specific item
Button(onClick = { groupState.expand(0) }) {
Text("Expand First")
}
Advanced Examples
FAQ Accordion
data class FAQ(val question: String, val answer: String)
val faqs = listOf(
FAQ("What is Lumo UI?", "Lumo UI is a Compose Multiplatform UI library..."),
FAQ("How do I install?", "Add the dependency to your build.gradle.kts...")
)
val groupState = rememberAccordionGroupState(
count = faqs.size,
allowMultipleOpen = false
)
Column {
faqs.forEachIndexed { index, faq ->
Accordion(
state = groupState.getState(index),
headerContent = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = faq.question,
style = AppTheme.typography.h3
)
Icon(
imageVector = if (groupState.getState(index).expanded) {
Icons.Default.ExpandLess
} else {
Icons.Default.ExpandMore
},
contentDescription = null
)
}
},
bodyContent = {
Text(
text = faq.answer,
modifier = Modifier.padding(16.dp),
style = AppTheme.typography.body1
)
}
)
if (index < faqs.size - 1) {
HorizontalDivider()
}
}
}
Styled Accordion with Card
Card(
modifier = Modifier.fillMaxWidth()
) {
Accordion(
headerContent = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Info,
contentDescription = null,
modifier = Modifier.padding(end = 16.dp)
)
Text(
text = "Details",
style = AppTheme.typography.h3
)
}
},
bodyContent = {
Column(modifier = Modifier.padding(16.dp)) {
Text("Detail 1")
Spacer(modifier = Modifier.height(8.dp))
Text("Detail 2")
Spacer(modifier = Modifier.height(8.dp))
Text("Detail 3")
}
}
)
}
Features
- Smooth Animation: Expandable animation with customizable specs
- State Management: Flexible state control and callbacks
- Group Behavior: Single or multiple open accordions
- Animation Progress: Access to animation progress for custom animations
- Accessibility: Proper semantics with role and state description
- Ripple Effect: Visual feedback on header click
- Flexible Content: Any composable can be used in header and body
Best Practices
- Clear Headers: Use descriptive header content
- Visual Indicators: Include expand/collapse icons
- Dividers: Use dividers between accordion items
- Animation: Keep animations enabled for better UX
- Group Management: Use AccordionGroupState for related items
Source Reference
Accordion component implementation: components-lab/src/commonMain/kotlin/com/nomanr/lumo/ui/components/Accordion.kt