Skip to main content
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
headerModifier
Modifier
default:"Modifier"
Modifier to be applied to the header section
state
AccordionState
default:"rememberAccordionState()"
State object that controls expansion and animation
animate
Boolean
default:"true"
Whether to animate the expansion/collapse
interactionSource
MutableInteractionSource
default:"MutableInteractionSource()"
Source of interactions for the accordion
headerContent
@Composable () -> Unit
required
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:
expanded
Boolean
default:"false"
Initial expansion state
enabled
Boolean
default:"true"
Whether the accordion can be toggled
clickable
Boolean
default:"true"
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

count
Int
required
Number of accordions in the group
allowMultipleOpen
Boolean
default:"false"
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

  1. Clear Headers: Use descriptive header content
  2. Visual Indicators: Include expand/collapse icons
  3. Dividers: Use dividers between accordion items
  4. Animation: Keep animations enabled for better UX
  5. Group Management: Use AccordionGroupState for related items

Source Reference

Accordion component implementation: components-lab/src/commonMain/kotlin/com/nomanr/lumo/ui/components/Accordion.kt

Build docs developers (and LLMs) love