Use bottom sheets for contextual actions and short tasks that should stay close to the current screen.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
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.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.Copy
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Pencil
import com.composables.icons.lucide.Share
import com.composables.icons.lucide.Trash2
import com.composables.ui.components.BottomSheet
import com.composables.ui.components.BottomSheetDetent
import com.composables.ui.components.Button
import com.composables.ui.components.ButtonStyle
import com.composables.ui.components.Icon
import com.composables.ui.components.Text
import com.composables.ui.components.rememberBottomSheetState
import com.composables.ui.theme.colors
import com.composables.ui.theme.destructiveColor
import com.composeunstyled.ProvideContentColor
import com.composeunstyled.theme.Theme
@Composable
fun BottomSheetActionMenuExample() {
val sheetState = rememberBottomSheetState(
detents = listOf(BottomSheetDetent.Hidden, BottomSheetDetent.FullyExpanded),
)
Button(onClick = { sheetState.targetDetent = BottomSheetDetent.FullyExpanded }) {
Text("Open actions")
}
BottomSheet(
state = sheetState,
onDismissRequest = { sheetState.targetDetent = BottomSheetDetent.Hidden },
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Button(
onClick = { sheetState.targetDetent = BottomSheetDetent.Hidden },
modifier = Modifier.fillMaxWidth(),
style = ButtonStyle.Secondary,
contentPadding = PaddingValues(horizontal = 16.dp),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text("Rename")
Spacer(Modifier.weight(1f))
Icon(
imageVector = Lucide.Pencil,
contentDescription = null,
modifier = Modifier.size(16.dp),
)
}
}
Button(
onClick = { sheetState.targetDetent = BottomSheetDetent.Hidden },
modifier = Modifier.fillMaxWidth(),
style = ButtonStyle.Secondary,
contentPadding = PaddingValues(horizontal = 16.dp),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text("Duplicate")
Spacer(Modifier.weight(1f))
Icon(
imageVector = Lucide.Copy,
contentDescription = null,
modifier = Modifier.size(16.dp),
)
}
}
Button(
onClick = { sheetState.targetDetent = BottomSheetDetent.Hidden },
modifier = Modifier.fillMaxWidth(),
style = ButtonStyle.Secondary,
contentPadding = PaddingValues(horizontal = 16.dp),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text("Share")
Spacer(Modifier.weight(1f))
Icon(
imageVector = Lucide.Share,
contentDescription = null,
modifier = Modifier.size(16.dp),
)
}
}
Button(
onClick = { sheetState.targetDetent = BottomSheetDetent.Hidden },
modifier = Modifier.fillMaxWidth(),
style = ButtonStyle.Secondary,
contentPadding = PaddingValues(horizontal = 16.dp),
) {
ProvideContentColor(Theme[colors][destructiveColor]) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text("Delete")
Spacer(Modifier.weight(1f))
Icon(
imageVector = Lucide.Trash2,
contentDescription = null,
modifier = Modifier.size(16.dp),
)
}
}
}
}
}
}Installation
implementation("com.composables:ui:0.1.0")Add the required dependencies
implementation("com.composables:composeunstyled:2.7.0")
Copy and paste the following sources into your project
components/BottomSheet.kt
package com.composables.ui.components
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.dropShadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.shadow.Shadow
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.composables.ui.theme.borderColor
import com.composables.ui.theme.colors
import com.composables.ui.theme.defaultIndication
import com.composables.ui.theme.ringColor
import com.composables.ui.theme.indications
import com.composables.ui.theme.onPanelColor
import com.composables.ui.theme.overlayShadow
import com.composables.ui.theme.panelColor
import com.composables.ui.theme.scrimColor
import com.composables.ui.theme.sheetShape
import com.composables.ui.theme.shadows
import com.composables.ui.theme.shapes
import com.composeunstyled.DragIndication
import com.composeunstyled.LocalTextStyle
import com.composeunstyled.ModalBottomSheetState
import com.composeunstyled.ProvideContentColor
import com.composeunstyled.ProvideTextStyle
import com.composeunstyled.Scrim
import com.composeunstyled.Sheet
import com.composeunstyled.SheetDetent
import com.composeunstyled.UnstyledModalBottomSheet
import com.composeunstyled.theme.Theme
import kotlin.jvm.JvmInline
import com.composeunstyled.rememberModalBottomSheetState as rememberUnstyledModalBottomSheetState
@Stable
class BottomSheetState internal constructor(
internal val unstyledState: ModalBottomSheetState,
) {
var targetDetent: BottomSheetDetent
get() = BottomSheetDetent.from(unstyledState.targetDetent)
set(value) {
unstyledState.targetDetent = value.unstyledDetent
}
/**
* Animates a BottomSheetState to the requested detent.
* @param detent Target detent to animate the sheet toward.
*/
suspend fun animateTo(detent: BottomSheetDetent) {
unstyledState.animateTo(detent.unstyledDetent)
}
/**
* Animates the bottom sheet to its fully expanded state.
*/
suspend fun show() {
animateTo(BottomSheetDetent.FullyExpanded)
}
/**
* Animates the bottom sheet to its hidden state.
*/
suspend fun hide() {
animateTo(BottomSheetDetent.Hidden)
}
}
/**
* Supported resting positions for the bottom sheet.
*/
@JvmInline
value class BottomSheetDetent internal constructor(
@Suppress("unused") private val value: Int,
) {
internal val unstyledDetent: SheetDetent
get() = when (this) {
FullyExpanded -> SheetDetent.FullyExpanded
else -> SheetDetent.Hidden
}
companion object {
/**
* Keeps the bottom sheet off screen.
*/
val Hidden = BottomSheetDetent(0)
/**
* Shows the bottom sheet at its expanded position.
*/
val FullyExpanded = BottomSheetDetent(1)
internal fun from(detent: SheetDetent): BottomSheetDetent {
return when (detent) {
SheetDetent.Hidden -> Hidden
SheetDetent.FullyExpanded -> FullyExpanded
else -> Hidden
}
}
}
}
/**
* Creates and remembers a [BottomSheetState] to be used in a [BottomSheet].
* @param initialDetent Detent to be used when the bottom sheet state is first created.
* @param detents Available detents that the bottom sheet can move between.
*/
@Composable
fun rememberBottomSheetState(
initialDetent: BottomSheetDetent = BottomSheetDetent.Hidden,
detents: List<BottomSheetDetent> = listOf(BottomSheetDetent.Hidden, BottomSheetDetent.FullyExpanded),
): BottomSheetState {
val unstyledState = rememberUnstyledModalBottomSheetState(
initialDetent = initialDetent.unstyledDetent,
detents = detents.map { it.unstyledDetent },
)
return remember(unstyledState) { BottomSheetState(unstyledState) }
}
/**
* A modal bottom sheet with optional header and footer content.
* @param state State object that controls the bottom sheet.
* @param onDismissRequest Called when the sheet should dismiss.
* @param modifier Modifier applied to the bottom sheet container.
* @param toolbar Optional header content shown at the top of the sheet.
* @param footer Optional footer content shown below the sheet body.
* @param shape Shape used for the sheet container.
* @param backgroundColor Background color used for the sheet surface.
* @param contentColor Color used for sheet content.
* @param contentPadding Padding applied around the sheet body content.
* @param shadow Shadow applied to the sheet container.
* @param body Main content displayed inside the sheet.
*/
@Composable
fun BottomSheet(
state: BottomSheetState,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
toolbar: (@Composable () -> Unit)? = null,
footer: (@Composable ColumnScope.() -> Unit)? = null,
shape: Shape = Theme[shapes][sheetShape],
backgroundColor: Color = Theme[colors][panelColor],
contentColor: Color = Theme[colors][onPanelColor],
contentPadding: PaddingValues = PaddingValues(20.dp),
shadow: Shadow = Theme[shadows][overlayShadow],
body: @Composable ColumnScope.() -> Unit,
) {
val dragIndicationInteractionSource = remember { MutableInteractionSource() }
UnstyledModalBottomSheet(
state = state.unstyledState,
onDismiss = onDismissRequest,
overlay = {
Scrim(
scrimColor = Theme[colors][scrimColor],
enter = fadeIn(tween(BottomSheetScrimAnimationDurationMillis)),
exit = fadeOut(tween(BottomSheetScrimAnimationDurationMillis)),
)
},
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
contentAlignment = Alignment.TopCenter,
) {
Sheet(
modifier = modifier
.widthIn(max = 640.dp)
.fillMaxWidth()
.dropShadow(shape, shadow)
.clip(shape)
.background(backgroundColor, shape),
) {
ProvideContentColor(contentColor) {
Box(
modifier = Modifier.fillMaxWidth(),
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
if (toolbar != null) {
ProvideTextStyle(LocalTextStyle.current.merge(BottomSheetHeaderTextStyle)) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp),
contentAlignment = Alignment.Center,
) {
toolbar()
}
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(contentPadding),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Column(
modifier = Modifier.fillMaxWidth(),
content = body,
)
if (footer != null) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp),
content = footer,
)
}
}
}
DragIndication(
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = 8.dp)
.size(width = 32.dp, height = 4.dp)
.focusRing(
interactionSource = dragIndicationInteractionSource,
color = Theme[colors][ringColor],
shape = RoundedCornerShape(100),
)
.background(Theme[colors][borderColor], RoundedCornerShape(100)),
indication = Theme[indications][defaultIndication],
interactionSource = dragIndicationInteractionSource,
)
}
}
}
}
}
}
private val BottomSheetHeaderTextStyle = TextStyle(
fontSize = 20.sp,
lineHeight = 24.sp,
fontWeight = FontWeight.Medium,
)
const val BottomSheetScrimAnimationDurationMillis = 300components/Utils.kt
package com.composables.ui.components
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.composables.ui.theme.colors
import com.composables.ui.theme.ringColor
import com.composeunstyled.FocusRingVisibility
import com.composeunstyled.collectIsFocusVisibleAsState
import com.composeunstyled.outline
import com.composeunstyled.theme.Theme
@Composable
fun Modifier.focusRing(
interactionSource: InteractionSource,
width: Dp = 2.dp,
color: Color = Theme[colors][ringColor],
shape: Shape = RectangleShape,
offset: Dp = 0.dp,
visibility: FocusRingVisibility = FocusRingVisibility.FocusVisible,
): Modifier {
val showFocusRing by if (visibility == FocusRingVisibility.FocusVisible) {
interactionSource.collectIsFocusVisibleAsState()
} else {
interactionSource.collectIsFocusedAsState()
}
val animatedWidth by animateDpAsState(
targetValue = if (showFocusRing) width else 0.dp,
animationSpec = tween(durationMillis = 120),
label = "FocusRingWidth",
)
return this then Modifier.outline(
width = animatedWidth,
color = color,
shape = shape,
offset = offset,
)
}
@Composable
fun Modifier.bouncyPress(
interactionSource: InteractionSource,
enabled: Boolean = true,
pressedScale: Float = 0.98f,
): Modifier {
val pressed by interactionSource.collectIsPressedAsState()
val scale by animateFloatAsState(
targetValue = if (enabled && pressed) pressedScale else 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMediumLow,
),
label = "BouncyPressScale",
)
return this then Modifier.graphicsLayer {
scaleX = scale
scaleY = scale
}
}Examples
Confirmation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.composables.icons.lucide.Bell
import com.composables.icons.lucide.Lucide
import com.composables.ui.components.BottomSheet
import com.composables.ui.components.BottomSheetDetent
import com.composables.ui.components.Button
import com.composables.ui.components.ButtonStyle
import com.composables.ui.components.Icon
import com.composables.ui.components.Text
import com.composables.ui.components.rememberBottomSheetState
import com.composables.ui.theme.colors
import com.composables.ui.theme.mutedColor
import com.composeunstyled.LocalTextStyle
import com.composeunstyled.ProvideContentColor
import com.composeunstyled.ProvideTextStyle
import com.composeunstyled.theme.Theme
@Composable
fun BottomSheetConfirmationExample() {
val sheetState = rememberBottomSheetState(
detents = listOf(BottomSheetDetent.Hidden, BottomSheetDetent.FullyExpanded),
)
Button(onClick = { sheetState.targetDetent = BottomSheetDetent.FullyExpanded }) {
Text("Show confirmation")
}
BottomSheet(
state = sheetState,
onDismissRequest = { sheetState.targetDetent = BottomSheetDetent.Hidden },
toolbar = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Icon(
imageVector = Lucide.Bell,
contentDescription = null,
modifier = Modifier.size(28.dp),
)
Text("Allow notifications?")
}
},
footer = {
Button(
onClick = { sheetState.targetDetent = BottomSheetDetent.Hidden },
modifier = Modifier.fillMaxWidth(),
style = ButtonStyle.Primary,
) {
Text("Allow")
}
Button(
onClick = { sheetState.targetDetent = BottomSheetDetent.Hidden },
modifier = Modifier.fillMaxWidth(),
style = ButtonStyle.Secondary,
) {
Text("Maybe later")
}
},
) {
ProvideContentColor(Theme[colors][mutedColor]) {
ProvideTextStyle(LocalTextStyle.current.merge(TextStyle(fontSize = 16.sp, lineHeight = 24.sp))) {
Text(
text = "Get notified when projects finish syncing, comments mention you, or billing needs attention.",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
)
}
}
}
}Form
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.path
import androidx.compose.ui.unit.dp
import com.composables.ui.components.BottomSheet
import com.composables.ui.components.BottomSheetDetent
import com.composables.ui.components.Button
import com.composables.ui.components.ButtonStyle
import com.composables.ui.components.Icon
import com.composables.ui.components.Text
import com.composables.ui.components.TextField
import com.composables.ui.components.rememberBottomSheetState
import com.composables.ui.theme.colors
import com.composables.ui.theme.mutedColor
import com.composeunstyled.theme.Theme
@Composable
fun BottomSheetFormExample() {
val nameState = rememberTextFieldState("Alex Styl")
val usernameState = rememberTextFieldState("alexstyl")
val sheetState = rememberBottomSheetState(
detents = listOf(BottomSheetDetent.Hidden, BottomSheetDetent.FullyExpanded),
)
Button(onClick = { sheetState.targetDetent = BottomSheetDetent.FullyExpanded }) {
Text("Edit name")
}
BottomSheet(
state = sheetState,
onDismissRequest = { sheetState.targetDetent = BottomSheetDetent.Hidden },
toolbar = { Text("Edit name") },
footer = {
Button(
onClick = { sheetState.targetDetent = BottomSheetDetent.Hidden },
modifier = Modifier.fillMaxWidth(),
style = ButtonStyle.Primary,
) {
Text("Save")
}
},
) {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text("Name")
TextField(
state = nameState,
modifier = Modifier.fillMaxWidth(),
accessibilityLabel = "Name",
leading = {
Icon(
imageVector = User,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = Theme[colors][mutedColor],
)
},
)
}
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text("Username")
TextField(
state = usernameState,
modifier = Modifier.fillMaxWidth(),
accessibilityLabel = "Username",
leading = {
Icon(
imageVector = AtSign,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = Theme[colors][mutedColor],
)
},
)
}
}
}
}
val AtSign: ImageVector
get() {
if (_AtSign != null) return _AtSign!!
_AtSign = ImageVector.Builder(
name = "at-sign",
defaultWidth = 24.dp,
defaultHeight = 24.dp,
viewportWidth = 24f,
viewportHeight = 24f
).apply {
path(
fill = SolidColor(Color.Transparent),
stroke = SolidColor(Color.Black),
strokeLineWidth = 2f,
strokeLineCap = StrokeCap.Round,
strokeLineJoin = StrokeJoin.Round
) {
moveTo(16f, 12f)
arcTo(4f, 4f, 0f, false, true, 12f, 16f)
arcTo(4f, 4f, 0f, false, true, 8f, 12f)
arcTo(4f, 4f, 0f, false, true, 16f, 12f)
close()
}
path(
fill = SolidColor(Color.Transparent),
stroke = SolidColor(Color.Black),
strokeLineWidth = 2f,
strokeLineCap = StrokeCap.Round,
strokeLineJoin = StrokeJoin.Round
) {
moveTo(16f, 8f)
verticalLineToRelative(5f)
arcToRelative(3f, 3f, 0f, false, false, 6f, 0f)
verticalLineToRelative(-1f)
arcToRelative(10f, 10f, 0f, true, false, -4f, 8f)
}
}.build()
return _AtSign!!
}
private var _AtSign: ImageVector? = null
val User: ImageVector
get() {
if (_User != null) return _User!!
_User = ImageVector.Builder(
name = "user",
defaultWidth = 24.dp,
defaultHeight = 24.dp,
viewportWidth = 24f,
viewportHeight = 24f
).apply {
path(
fill = SolidColor(Color.Transparent),
stroke = SolidColor(Color.Black),
strokeLineWidth = 2f,
strokeLineCap = StrokeCap.Round,
strokeLineJoin = StrokeJoin.Round
) {
moveTo(19f, 21f)
verticalLineToRelative(-2f)
arcToRelative(4f, 4f, 0f, false, false, -4f, -4f)
horizontalLineTo(9f)
arcToRelative(4f, 4f, 0f, false, false, -4f, 4f)
verticalLineToRelative(2f)
}
path(
fill = SolidColor(Color.Transparent),
stroke = SolidColor(Color.Black),
strokeLineWidth = 2f,
strokeLineCap = StrokeCap.Round,
strokeLineJoin = StrokeJoin.Round
) {
moveTo(16f, 7f)
arcTo(4f, 4f, 0f, false, true, 12f, 11f)
arcTo(4f, 4f, 0f, false, true, 8f, 7f)
arcTo(4f, 4f, 0f, false, true, 16f, 7f)
close()
}
}.build()
return _User!!
}
private var _User: ImageVector? = nullAPI Reference
rememberBottomSheetState
Creates and remembers a [BottomSheetState] to be used in a BottomSheet.
@Composable
fun rememberBottomSheetState(
initialDetent: BottomSheetDetent = BottomSheetDetent.Hidden,
detents: List<BottomSheetDetent> = listOf(BottomSheetDetent.Hidden, BottomSheetDetent.FullyExpanded),
): BottomSheetState
| Parameter | Type | Description |
|---|---|---|
initialDetent |
BottomSheetDetent |
Detent to be used when the bottom sheet state is first created. |
detents |
List<BottomSheetDetent> |
Available detents that the bottom sheet can move between. |
BottomSheet
A modal bottom sheet with optional header and footer content.
@Composable
fun BottomSheet(
state: BottomSheetState,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
toolbar: (@Composable () -> Unit)? = null,
footer: (@Composable ColumnScope.() -> Unit)? = null,
shape: Shape = Theme[shapes][sheetShape],
backgroundColor: Color = Theme[colors][panelColor],
contentColor: Color = Theme[colors][onPanelColor],
contentPadding: PaddingValues = PaddingValues(20.dp),
shadow: Shadow = Theme[shadows][overlayShadow],
body: @Composable ColumnScope.() -> Unit,
)
| Parameter | Type | Description |
|---|---|---|
state |
BottomSheetState |
State object that controls the bottom sheet. |
onDismissRequest |
() -> Unit |
Called when the sheet should dismiss. |
modifier |
Modifier |
Modifier applied to the bottom sheet container. |
toolbar |
(@Composable () -> Unit)? |
Optional header content shown at the top of the sheet. |
footer |
(@Composable ColumnScope.() -> Unit)? |
Optional footer content shown below the sheet body. |
shape |
Shape |
Shape used for the sheet container. |
backgroundColor |
Color |
Background color used for the sheet surface. |
contentColor |
Color |
Color used for sheet content. |
contentPadding |
PaddingValues |
Padding applied around the sheet body content. |
shadow |
Shadow |
Shadow applied to the sheet container. |
body |
@Composable ColumnScope.() -> Unit |
Main content displayed inside the sheet. |
BottomSheetDetent
Supported resting positions for the bottom sheet.
@JvmInline
value class BottomSheetDetent internal constructor(
@Suppress("unused") private val value: Int,
)
| Value | Description |
|---|---|
Hidden |
Keeps the bottom sheet off screen. |
FullyExpanded |
Shows the bottom sheet at its expanded position. |
animateTo
Animates a BottomSheetState to the requested detent.
suspend fun animateTo(detent: BottomSheetDetent)
| Parameter | Type | Description |
|---|---|---|
detent |
BottomSheetDetent |
Target detent to animate the sheet toward. |
show
Animates the bottom sheet to its fully expanded state.
suspend fun show()
hide
Animates the bottom sheet to its hidden state.
suspend fun hide()