Compose Unstyled 2.0 is out! Check the official announcement blog ->

Disclosure

Expandable sections with a button and an animated content panel.

Use disclosures when content should stay collapsed until the user asks to reveal more.

View on GitHub
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
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.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.ChevronDown
import com.composables.icons.lucide.Lucide
import com.composables.ui.components.Disclosure
import com.composables.ui.components.DisclosureButton
import com.composables.ui.components.DisclosurePanel
import com.composables.ui.components.Icon
import com.composables.ui.components.Text

@Composable
fun DisclosureExample() {
    var expanded by remember { mutableStateOf(false) }
    Disclosure(
        expanded = expanded,
        onExpandedChange = { expanded = it },
        modifier = Modifier.fillMaxWidth()
    ) {
        DisclosureButton(modifier = Modifier.fillMaxWidth()) {
            Text("What is your return policy?")
        }
        DisclosurePanel {
            Text("Returns are accepted within 30 days in original condition. Refunds are issued to the original payment method.")
        }
    }
}

Installation

implementation("com.composables:ui:0.1.0")

Examples

With an indicator

View on GitHub
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
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.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.ChevronDown
import com.composables.icons.lucide.Lucide
import com.composables.ui.components.Disclosure
import com.composables.ui.components.DisclosureButton
import com.composables.ui.components.DisclosurePanel
import com.composables.ui.components.Icon
import com.composables.ui.components.Text

@Composable
fun DisclosureWithIndicatorExample() {
    var expanded by remember { mutableStateOf(false) }
    Disclosure(
        expanded = expanded,
        onExpandedChange = { expanded = it },
    ) {
        DisclosureButton(
            indicator = {
                val rotation by animateFloatAsState(
                    targetValue = if (it) -180f else 0f,
                )
                Icon(
                    imageVector = Lucide.ChevronDown,
                    modifier = Modifier
                        .size(16.dp)
                        .rotate(rotation),
                )
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("What is your return policy?")
        }
        DisclosurePanel {
            Text("Returns are accepted within 30 days in original condition. Refunds are issued to the original payment method.")
        }
    }
}

Disabled

View on GitHub
import androidx.compose.foundation.layout.fillMaxWidth
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.Modifier
import com.composables.ui.components.Disclosure
import com.composables.ui.components.DisclosureButton
import com.composables.ui.components.DisclosurePanel
import com.composables.ui.components.Text

@Composable
fun DisabledDisclosureExample() {
    var expanded by remember { mutableStateOf(false) }
    Disclosure(
        expanded = expanded,
        onExpandedChange = { expanded = it },
    ) {
        DisclosureButton(enabled = false, modifier = Modifier.fillMaxWidth()) {
            Text("Show installation notes")
        }
        DisclosurePanel {
            Text("Wall mounting requires a solid surface, a level, and two anchor points spaced 40 cm apart.")
        }
    }
}

API Reference

Disclosure

A container that coordinates disclosure state and content.

@Composable
fun Disclosure(
    expanded: Boolean,
    onExpandedChange: (Boolean) -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
)
Parameter Type Description
expanded Boolean Whether the disclosure content is expanded.
onExpandedChange (Boolean) -> Unit Called when the disclosure expanded state changes.
modifier Modifier Modifier applied to the component.
content @Composable () -> Unit Composable content displayed by the component.

DisclosureButton

A button that toggles the disclosure between expanded and collapsed.

@Composable
fun DisclosureButton(
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    contentPadding: PaddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
    indicator: (@Composable (expanded: Boolean) -> Unit)? = null,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit,
)
Parameter Type Description
modifier Modifier Modifier applied to the component.
enabled Boolean Whether the disclosure button can be interacted with.
contentPadding PaddingValues Padding applied inside the component content.
indicator (@Composable (expanded: Boolean) -> Unit)? Optional trailing indicator that receives the expanded state.
interactionSource MutableInteractionSource Interaction source used for focus and press state.
content @Composable RowScope.() -> Unit Composable content displayed by the component.

DisclosurePanel

The expandable content region of a disclosure.

@Composable
fun DisclosurePanel(
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(12.dp),
    enter: EnterTransition = expandVertically(animationSpec = spring()) + fadeIn(),
    exit: ExitTransition = shrinkVertically(animationSpec = spring()) + fadeOut(),
    content: @Composable () -> Unit,
)
Parameter Type Description
modifier Modifier Modifier applied to the component.
contentPadding PaddingValues Padding applied inside the component content.
enter EnterTransition Transition used when the disclosure panel expands.
exit ExitTransition Transition used when the disclosure panel collapses.
content @Composable () -> Unit Composable content displayed by the component.