Bottom Sheets

Bottom sheets are useful for displaying contained information within a thumbs reach.

Bottom Sheet

import androidx.compose.foundation.horizontalScroll
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
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.layout.ContentScale
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.composables.core.SheetDetent
import com.composables.core.rememberBottomSheetState
import com.composables.icons.lucide.Clock3
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Navigation
import com.composables.icons.lucide.PhoneCall
import com.composables.icons.lucide.Share2
import com.composables.icons.lucide.Star
import com.composables.uikit.components.BottomSheet
import com.composables.uikit.components.Icon
import com.composables.uikit.components.OutlinedButton
import com.composables.uikit.components.PrimaryButton
import com.composables.uikit.components.SecondaryButton
import com.composables.uikit.components.Text
import com.composables.uikit.styling.medium
import com.composables.uikit.styling.shapes
import com.composables.uikit.styling.textStyles
import com.composables.uikit.styling.title
import com.composeunstyled.theme.Theme

@Composable
fun BottomSheetExample() {
    val imagePreviews = listOf(
        "https://images.unsplash.com/photo-1620807773206-49c1f2957417?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1583968300292-46b9da50309d?q=80&w=3687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1516650144360-23d63d35d308?q=80&w=2000&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1536319229365-83318cdc7b83?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
    )
    val Peek = SheetDetent("peek") { containerHeight: Dp, sheetHeight: Dp ->
        280.dp
    }
    val bottomSheetState = rememberBottomSheetState(
        initialDetent = Peek,
        detents = listOf(SheetDetent.Hidden, Peek, SheetDetent.FullyExpanded),
    )
    Box(Modifier.fillMaxSize()) {

        PrimaryButton(
            onClick = { bottomSheetState.targetDetent = Peek },
            modifier = Modifier.align(Alignment.Center),
        ) {
            Text("Show Details")
        }

        BottomSheet(state = bottomSheetState) {
            Column(Modifier.fillMaxWidth()) {
                Text(
                    "Bloom & Brew", style = Theme[textStyles][title],
                    modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
                )
                Spacer(Modifier.height(12.dp))
                Column(
                    modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    Text("42 Brick Lane, London E1 6RF, UK")
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        Icon(
                            Lucide.Star,
                            contentDescription = "Staring",
                            modifier = Modifier.size(16.dp),
                            tint = Color(0xFFFACC15)
                        )
                        Spacer(Modifier.width(8.dp))
                        Text("4.7 (842 reviews)")
                    }
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        Icon(
                            Lucide.Clock3,
                            contentDescription = null,
                            modifier = Modifier.size(16.dp),
                        )
                        Spacer(Modifier.width(8.dp))
                        Text("Open now · Closes 8:00 PM")
                    }
                }
                Spacer(Modifier.height(12.dp))
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .horizontalScroll(rememberScrollState())
                        .padding(horizontal = 16.dp),
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    PrimaryButton(onClick = {}) {
                        Icon(Lucide.Navigation, contentDescription = null)
                        Text("Directions")
                    }
                    SecondaryButton(onClick = {}) {
                        Icon(Lucide.PhoneCall, contentDescription = null)
                        Text("Call")
                    }
                    SecondaryButton(onClick = {}) {
                        Icon(Lucide.Share2, contentDescription = null)
                        Text("Share")
                    }
                    SecondaryButton(onClick = {}) {
                        Icon(Lucide.Star, contentDescription = null)
                        Text("Star it")
                    }
                }

                Spacer(Modifier.height(12.dp))
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .horizontalScroll(rememberScrollState())
                        .padding(horizontal = 16.dp),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    imagePreviews.forEach { preview ->
                        AsyncImage(
                            model = preview,
                            contentDescription = null,
                            modifier = Modifier.clip(Theme[shapes][medium]).size(190.dp),
                            contentScale = ContentScale.Crop
                        )
                    }
                }

            }
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.unit.dp
import com.composables.uikit.components.Icon
import com.composables.core.SheetDetent
import com.composables.core.rememberModalBottomSheetState
import com.composables.uikit.components.GhostButton
import com.composables.uikit.components.ModalBottomSheet
import com.composables.uikit.components.PrimaryButton
import com.composables.uikit.styling.destructive
import com.composables.icons.lucide.Link2
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Pencil
import com.composables.icons.lucide.Share2
import com.composables.icons.lucide.Trash2
import com.composables.uikit.components.Text
import com.composables.uikit.styling.colors
import com.composeunstyled.theme.Theme


@Composable
fun ModalBottomSheetExample() {
    val bottomSheetState = rememberModalBottomSheetState(
        initialDetent = SheetDetent.Hidden,
        detents = listOf(SheetDetent.Hidden, SheetDetent.FullyExpanded),
    )
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        PrimaryButton(onClick = { bottomSheetState.targetDetent = SheetDetent.FullyExpanded }) {
            Text("Show options")
        }
        ModalBottomSheet(state = bottomSheetState) {
            Column(Modifier.fillMaxWidth()) {
                GhostButton(
                    onClick = { /* TODO */ },
                    modifier = Modifier.fillMaxWidth(),
                    shape = RectangleShape,
                    horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.Start),
                    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
                ) {
                    Icon(Lucide.Pencil, contentDescription = null)
                    Text("Edit")
                }

                GhostButton(
                    onClick = { /* TODO */ },
                    modifier = Modifier.fillMaxWidth(),
                    shape = RectangleShape,
                    horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.Start),
                    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
                ) {
                    Icon(Lucide.Link2, contentDescription = null)
                    Text("Copy Link")
                }

                GhostButton(
                    onClick = { /* TODO */ },
                    modifier = Modifier.fillMaxWidth(),
                    shape = RectangleShape,
                    horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.Start),
                    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
                ) {
                    Icon(Lucide.Share2, contentDescription = null)
                    Text("Share")
                }

                GhostButton(
                    onClick = { /* TODO */ },
                    modifier = Modifier.fillMaxWidth(),
                    shape = RectangleShape,
                    horizontalArrangement = Arrangement.spacedBy(24.dp, Alignment.Start),
                    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
                    contentColor = Theme[colors][destructive],
                ) {
                    Icon(Lucide.Trash2, contentDescription = null)
                    Text("Delete")
                }
            }
        }
    }
}