Sheet
A panel over the app for a short task that belongs to the current screen: choosing how to sort, where to share, a field or two, a list to pick from, the details of what was pressed.
import com.pantrixui.components.overlay.sheet.PantrixSheet
- Along the bottom edge. The default edge is
SheetEdge.Bottom: the sheet is as wide as the window, up to the style'smaxWidth, and as tall as what it holds. A drag moves it: a drag down closes it. - Side sheet. With
edge = SheetEdge.Endthe sheet stands at the end edge, on the left in a language written right to left: it runs the length of the window, is as wide as the style'ssideWidthand has a close button. A drag towards the edge closes it.SheetEdge.Startopens the same sheet from the start edge: an app's menu in a narrow window. - Edge-to-edge content. The content spans the sheet's width: the rows of a list reach its edges, and a form brings its own horizontal padding. Content that does not fit in the sheet scrolls under the title.
- Half open. With
halfOpena sheet whose content is taller than half the window first opens half open, its top at the middle of the window. A drag up, or focus moving into it, opens it fully; a drag down from half open closes it. - Full screen. With
fullScreenthe fully open sheet is as tall as the window under the status bar, however short its content.coverStatusBartakes it under the status bar too. - A list that scrolls itself. A long list belongs in a
LazyColumn, withscrollContent = false.overscrollMovesSheet = falsemakes the list do nothing but scroll: once it is at its top it does not pull the sheet down, and the handle and the title still drag the sheet. - A sheet that cannot be dismissed. With
dismissible = falsea drag, the scrim, back and Escape do nothing; a side sheet has no close button, and a sheet that does not open half open has no handle either. The way out is a control in the content. - It stays in the tree as it closes. A closed sheet composes nothing: it stays in the tree with
visible = falseand leaves with the theme's motion. A sheet taken out of the composition while it is open is gone at once, without the motion. - Colour and style.
colorschanges the fill, the title, the handle, the close icon and the scrim;stylethe corners that face the app, the widths, the margin kept for the scrim, the shadow and the handle. The corners are large on Android and iOS and medium on desktop and the web; there is no shadow on iOS.
Examples
Sorting
At its plainest: a title and content. A choice is the whole task: the sheet closes as soon as it is made, and there is no need for a separate "OK" button.
The sheet never closes itself: a drag down, a click on the scrim, back and Escape call onDismissRequest; in every case it is the open = false line that closes it. The card's "Last event" line says what happened last.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
var sort by rememberSaveable { mutableStateOf("name") }
PantrixButton(
text = "Sort by: ${SORTS.first { it.value == sort }.label}",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_sort"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Sort sheet closed")
},
title = "Sort by",
tagKey = "sort_by"
) {
PantrixRadioGroup(
options = SORTS,
selected = sort,
onSelect = {
sort = it
open = false
report("Sort order: ${SORTS.first { option -> option.value == it }.label}")
},
modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
tagKey = "sort"
)
}
private val SORTS = listOf(
RadioOption(value = "name", label = "Name", tagKey = "name"),
RadioOption(value = "date", label = "Date modified", tagKey = "date"),
RadioOption(value = "size", label = "Size", tagKey = "size")
)
Form
A field and a button. The content reaches the sheet's edges, so the form brings its own horizontal padding: Modifier.padding(horizontal = …). "Save" is disabled while the note is empty.
When the sheet is opened by touch, focus does not move to the field. Once the field is pressed, the sheet stays above the keyboard.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
val note = rememberTextFieldState()
PantrixButton(
text = "Add note…",
onClick = {
note.clearText()
open = true
},
variant = ButtonVariant.Outline,
tagKey = "open_note"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Note sheet closed")
},
title = "Add note",
tagKey = "note"
) {
Column(
modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.lg)
) {
PantrixTextField(state = note, modifier = Modifier.fillMaxWidth(), label = "Note", tagKey = "note")
PantrixButton(
text = "Save",
onClick = {
open = false
report("Note: ${note.text}")
},
modifier = Modifier.fillMaxWidth(),
enabled = note.text.isNotBlank(),
tagKey = "save_note"
)
}
}
With the keyboard open
A sheet opened while typing in a field of the page. Focus stays wherever it is on the page, and is there again when the sheet closes; where that is depends on the platform. On Android and iOS a tap on the button takes no focus: focus stays in the field that was typed in. On desktop and the web a press on the button moves focus to the button: when the sheet closes, focus is on the button.
While the sheet is open, what is typed on the keyboard goes to the sheet, not to the field underneath; meanwhile the field is drawn at rest, without its caret. On Android and iOS the on-screen keyboard closes when the sheet opens; when the sheet closes, the caret and the keyboard return to the field.
Type in the field first, then press the button: what was typed in the field stays as it is on every platform.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
val search = rememberTextFieldState()
PantrixTextField(
state = search,
modifier = Modifier.widthIn(max = 360.dp).fillMaxWidth(),
label = "Search",
tagKey = "page_search"
)
PantrixButton(
text = "Filters…",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_filters"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Filter sheet closed")
},
title = "Filters",
tagKey = "filters"
) {
FILTERS.forEachIndexed { index, filter ->
SheetRow(
text = filter,
tag = "Row_filter_$index",
onClick = {
open = false
report("Filter: $filter")
}
)
}
}
@Composable
private fun SheetRow(
text: String,
tag: String,
onClick: () -> Unit
) {
val spacing = PantrixTheme.spacing
Box(
modifier = Modifier
.fillMaxWidth()
.testTag(tag)
.clickable(onClick = onClick)
.padding(horizontal = spacing.lg, vertical = spacing.md)
) {
PantrixText(text = text)
}
}
private val FILTERS = listOf("Shared only", "Last 7 days", "PDF files")
Half-open list
A long list that starts half open: the top of it is usually enough. With halfOpen the sheet opens up to the middle of the window; a drag up, or focus moving into it, opens it fully. fullScreen makes the fully open sheet as tall as the window under the status bar.
The list scrolls itself: it is in a LazyColumn, and scrollContent = false turns off the sheet's own scroll area. The list is given Modifier.fillMaxWidth(): without it, a drag beside its rows would not scroll it. With overscrollMovesSheet = false a list that has reached its top does not pull the sheet down; the handle and the title still drag the sheet.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
PantrixButton(
text = "Choose a country…",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_countries"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Country sheet closed")
},
title = "Country",
halfOpen = true,
fullScreen = true,
overscrollMovesSheet = false,
scrollContent = false,
tagKey = "country"
) {
LazyColumn(
modifier = Modifier.fillMaxWidth().testTag("List_countries"),
contentPadding = PaddingValues(bottom = PantrixTheme.spacing.lg)
) {
itemsIndexed(COUNTRIES) { index, country ->
SheetRow(
text = country,
tag = "Row_country_$index",
onClick = {
open = false
report("Country: $country")
}
)
}
}
}
@Composable
private fun SheetRow(
text: String,
tag: String,
onClick: () -> Unit
) {
val spacing = PantrixTheme.spacing
Box(
modifier = Modifier
.fillMaxWidth()
.testTag(tag)
.clickable(onClick = onClick)
.padding(horizontal = spacing.lg, vertical = spacing.md)
) {
PantrixText(text = text)
}
}
private val COUNTRIES = (
"Argentina, Australia, Austria, Belgium, Brazil, Bulgaria, Canada, Chile, China, Colombia, Czechia, " +
"Denmark, Egypt, Estonia, Finland, France, Germany, Greece, Hungary, Iceland, India, Indonesia, Ireland, " +
"Italy, Japan, Kenya, Latvia, Lithuania, Malaysia, Mexico, Morocco, Netherlands, New Zealand, Norway, " +
"Peru, Poland, Portugal, Romania, Singapore, Slovakia, South Africa, South Korea, Spain, Sweden, " +
"Switzerland, Thailand, Tunisia, Türkiye, Ukraine, United Kingdom, Vietnam"
).split(", ")
Full screen
Like a screen of its own: with fullScreen the fully open sheet is as tall as the window under the status bar, however short its content. With coverStatusBar it reaches under the status bar too and is as tall as the whole window: its top corners square off there, and its handle and title stay below the bar.
The files are filtered as you type in the field; the sheet's height does not change even when the list gets shorter. The search starts empty each time the sheet opens.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
val query = rememberTextFieldState()
PantrixButton(
text = "Search files…",
onClick = {
query.clearText()
open = true
},
variant = ButtonVariant.Outline,
tagKey = "open_search"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Search sheet closed")
},
title = "Search files",
fullScreen = true,
coverStatusBar = true,
tagKey = "search"
) {
PantrixTextField(
state = query,
modifier = Modifier.fillMaxWidth().padding(horizontal = PantrixTheme.spacing.lg),
label = "Search",
tagKey = "query"
)
FILES.forEachIndexed { index, file ->
if (query.text.isBlank() || file.contains(query.text, ignoreCase = true)) {
SheetRow(
text = file,
tag = "Row_file_$index",
onClick = {
open = false
report("Opened file: $file")
}
)
}
}
}
@Composable
private fun SheetRow(
text: String,
tag: String,
onClick: () -> Unit
) {
val spacing = PantrixTheme.spacing
Box(
modifier = Modifier
.fillMaxWidth()
.testTag(tag)
.clickable(onClick = onClick)
.padding(horizontal = spacing.lg, vertical = spacing.md)
) {
PantrixText(text = text)
}
}
private val FILES = listOf("Report.pdf", "Budget.xlsx", "Presentation.key", "Notes.txt", "Contract.pdf", "Logo.svg")
A required choice
dismissible = false: no handle is drawn; a drag, the scrim, back and Escape do nothing, and onDismissRequest is not called. The buttons inside are the way out, so there is a button for every way out. It is used only where a control in the sheet's content must close it.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
PantrixButton(
text = "Notification permission",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_permission"
)
PantrixSheet(
visible = open,
onDismissRequest = { report("This is never called: the sheet cannot be dismissed") },
title = "Allow notifications",
dismissible = false,
tagKey = "permission"
) {
Column(
modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)
) {
PantrixText(text = "Shall we let you know when shared files change?")
PantrixButton(
text = "Allow",
onClick = {
open = false
report("Notifications allowed")
},
modifier = Modifier.fillMaxWidth(),
tagKey = "allow"
)
PantrixButton(
text = "Not now",
onClick = {
open = false
report("Notification permission postponed")
},
modifier = Modifier.fillMaxWidth(),
variant = ButtonVariant.Text,
tagKey = "not_now"
)
}
}
Side sheet
edge = SheetEdge.End: the sheet comes in from the window's end edge, the right one in a left-to-right language, and is as tall as the window. It is for the details of what was pressed: the list stays where it is, and the details come in from the side.
The close button, the scrim, back, Escape and a drag towards the edge close it. A side sheet does not open half open and is never full screen: those parameters are for a sheet along the bottom edge only.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
PantrixButton(
text = "Report.pdf details",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_details"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Details sheet closed")
},
title = "Report.pdf",
edge = SheetEdge.End,
tagKey = "details"
) {
Column(
modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)
) {
DETAILS.forEach { (name, value) ->
Column {
PantrixText(
text = name,
style = PantrixTheme.typography.footnote,
color = PantrixTheme.colors.textSecondary
)
PantrixText(text = value)
}
}
}
}
private val DETAILS = listOf(
"Type" to "PDF document",
"Size" to "2,4 MB",
"Modified" to "3 October 2026",
"Owner" to "Ada Yılmaz",
"Location" to "Documents / Reports"
)
A form in a side sheet
The place for a form in a wide window: the page stays in view behind it. When it is opened with the keyboard, focus is in the first field; Tab goes round inside the sheet. "Save" is disabled while the name is empty.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
val name = rememberTextFieldState(initialText = "Ada Yılmaz")
val title = rememberTextFieldState(initialText = "Designer")
PantrixButton(
text = "Edit profile…",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_profile"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Profile sheet closed")
},
title = "Edit profile",
edge = SheetEdge.End,
tagKey = "profile"
) {
Column(
modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.lg)
) {
PantrixTextField(state = name, modifier = Modifier.fillMaxWidth(), label = "Name", tagKey = "name")
PantrixTextField(state = title, modifier = Modifier.fillMaxWidth(), label = "Title", tagKey = "title")
PantrixButton(
text = "Save",
onClick = {
open = false
report("Profile: ${name.text}, ${title.text}")
},
enabled = name.text.isNotBlank(),
tagKey = "save_profile"
)
}
}
Menu from the start edge
edge = SheetEdge.Start: the sheet comes from the window's start edge, from the left in a language written left to right. It is an app's menu in a narrow window: the navigation that stands at the side in a wide window opens on a phone from a button, and closes when a destination is chosen.
It is the side sheet the other way round: it runs the length of the window, has a close button, and a drag towards the start edge closes it.
var open by rememberSaveable { mutableStateOf(false) }
var where by rememberSaveable { mutableStateOf(SECTIONS.first()) }
PantrixButton(
text = "Menu",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_menu"
)
PantrixText(text = "Open section: $where", color = PantrixTheme.colors.textSecondary)
PantrixSheet(
visible = open,
onDismissRequest = { open = false },
title = "Docs",
edge = SheetEdge.Start,
tagKey = "menu"
) {
Column {
SECTIONS.forEachIndexed { index, section ->
SheetRow(text = section, tag = "menu_$index") {
where = section
open = false
}
}
}
}
@Composable
private fun SheetRow(
text: String,
tag: String,
onClick: () -> Unit
) {
val spacing = PantrixTheme.spacing
Box(
modifier = Modifier
.fillMaxWidth()
.testTag(tag)
.clickable(onClick = onClick)
.padding(horizontal = spacing.lg, vertical = spacing.md)
) {
PantrixText(text = text)
}
}
private val SECTIONS = listOf("Installation", "Theme", "Forms", "Testing")
Its own colours
The colours given with colors change, and the ones left out stay the theme's: here the fill, the title and the handle are in the app's own purple, and the scrim is the theme's.
No role of the theme has this purple, so the colour that reads on it is the caller's to give too: the title's through colors, and that of the text in the content through its own color. Had the fill been one of the theme's role colours, the title, the handle and the close icon would have taken the colour paired with that role by themselves.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
val own = ownColors()
PantrixButton(
text = "What you get with Pro",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_pro"
)
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("Pro sheet closed")
},
title = "Upgrade to Pro",
colors = SheetColors(container = own.container, title = own.onContainer, handle = own.color),
tagKey = "pro"
) {
Column(
modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)
) {
PantrixText(
text = "Unlimited projects, shared folders and priority support.",
color = own.onContainer
)
PantrixButton(
text = "Upgrade",
onClick = {
open = false
report("Plan upgraded")
},
tagKey = "upgrade_plan"
)
}
}
Its own style
style changes the geometry of this call alone: the corners that face the app have the theme's smallest rounding (ShapeRole.ExtraSmall), the sheet is 420dp wide at most, and the handle is 56dp by 6dp. In a window wider than 420dp the sheet stays in the middle of the bottom edge. The fields that are not set come from the platform's style.
var open by rememberSaveable { mutableStateOf(false) }
PantrixButton(
text = "Export…",
onClick = { open = true },
variant = ButtonVariant.Outline,
tagKey = "open_export"
)
PantrixSheet(
visible = open,
onDismissRequest = { open = false },
title = "Export",
style = SheetStyle(
shape = ShapeRole.ExtraSmall,
maxWidth = 420.dp,
handleWidth = 56.dp,
handleHeight = 6.dp
),
tagKey = "export"
) {
EXPORT_FORMATS.forEachIndexed { index, format ->
SheetRow(text = format, tag = "Row_export_$index", onClick = { open = false })
}
}
@Composable
private fun SheetRow(
text: String,
tag: String,
onClick: () -> Unit
) {
val spacing = PantrixTheme.spacing
Box(
modifier = Modifier
.fillMaxWidth()
.testTag(tag)
.clickable(onClick = onClick)
.padding(horizontal = spacing.lg, vertical = spacing.md)
) {
PantrixText(text = text)
}
}
private val EXPORT_FORMATS = listOf("PDF document", "Word document", "Plain text")
In a part in another theme
A part in the opposite colours to the page, and a sheet opened from inside it. The part is a second theme (PantrixTheme(darkTheme = …)); the sheet is called inside that theme too, so it is in that theme's colours: if the part is dark, so is the sheet.
var open by rememberSaveable { mutableStateOf(false) }
val report = LocalExampleEvents.current
val pageIsDark = PantrixTheme.colors.background.luminance() < 0.5f
PantrixTheme(darkTheme = !pageIsDark) {
PantrixSurface(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(PantrixTheme.spacing.md)) {
PantrixButton(text = "Open from this part", onClick = { open = true }, tagKey = "open_sheet_in_part")
}
}
PantrixSheet(
visible = open,
onDismissRequest = {
open = false
report("The part's sheet closed")
},
title = "Share",
tagKey = "share"
) {
SHARE_TARGETS.forEachIndexed { index, target ->
SheetRow(
text = target,
tag = "Row_share_$index",
onClick = {
open = false
report("Shared: $target")
}
)
}
}
}
@Composable
private fun SheetRow(
text: String,
tag: String,
onClick: () -> Unit
) {
val spacing = PantrixTheme.spacing
Box(
modifier = Modifier
.fillMaxWidth()
.testTag(tag)
.clickable(onClick = onClick)
.padding(horizontal = spacing.lg, vertical = spacing.md)
) {
PantrixText(text = text)
}
}
private val SHARE_TARGETS = listOf("Copy link", "Send by email", "Open to the team")
An edge by the window
edge = SheetEdge.Adaptive: a full-screen sheet from the bottom in a narrow window, a side sheet in a wider one. The place for a form or the details of what was pressed: it fills a phone, and stands beside the content on a tablet and a desktop. Switch between "Narrow" and "Wide"; a sheet that is open keeps its edge when the window changes.
var open by rememberSaveable { mutableStateOf(false) }
// The card's window is as tall as the example: the example is kept tall so that a sheet opened in a narrow window has room.
Column(Modifier.fillMaxWidth().height(280.dp)) {
PantrixButton(text = "Order 1042", onClick = { open = true }, tagKey = "open_adaptive_sheet")
PantrixSheet(
visible = open,
onDismissRequest = { open = false },
title = "Order 1042",
edge = SheetEdge.Adaptive,
tagKey = "adaptive_sheet"
) {
Column(
modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)
) {
PantrixText("Three items, delivered on Tuesday.")
PantrixText("No delivery charge; the invoice came by email.")
PantrixButton(text = "Close", onClick = { open = false }, tagKey = "adaptive_sheet_close")
}
}
}
API
visible
A panel over the app for a short task that belongs to the current screen: choosing how to sort, where to share, a field or two, a list to pick from, the details of what was pressed.
| Parameter | Type | Default | What it is |
|---|---|---|---|
visible | Boolean | whether the sheet is shown. It enters and leaves with the theme's motion. | |
onDismissRequest | () -> Unit | called for back, Escape, a click on the scrim, a drag towards the edge and the side sheet's close button while dismissible. The sheet stays until visible is false. | |
title | String? | null | what the sheet is for, as a short label: "Sort by". It is announced when the sheet appears and is its heading. Null or blank for none: no line is kept for it, the content starts under the handle, and a screen reader announces no name when the sheet appears. A side sheet's close button still stands at its top. |
modifier | Modifier | Modifier | applied to the sheet itself. The strip of scrim kept clear beside it is no part of it. |
edge | SheetEdge | SheetEdge.Bottom | the edge of the window the sheet lies along: the bottom, the end, the start, or by the window's width (SheetEdge.Adaptive), full screen from the bottom in a narrow one. halfOpen, fullScreen, coverStatusBar and overscrollMovesSheet count for an adaptive sheet where it comes from the bottom. |
dismissible | Boolean | true | whether back, Escape, a click on the scrim, a drag towards the edge, the side sheet's close button and a screen reader's dismiss action ask to close. Turn it off only when a control in content must close the sheet. |
halfOpen | Boolean | false | whether the sheet first opens half open, its top at the middle of the window, when what it holds is taller than that. A drag up opens it fully, and so does focus moving into it; a drag down from half open closes it. Along the bottom edge only. |
fullScreen | Boolean | false | whether the fully open sheet is as tall as the window under the status bar however short its content, with no scrim above it. Like every sheet along the bottom edge it reaches the window's bottom edge, under the system's navigation bar, and keeps what it holds above that bar. Along the bottom edge only. |
coverStatusBar | Boolean | false | whether a fullScreen sheet reaches under the status bar too, fully open as tall as the whole window. Its top corners square off as it slides under the bar, its handle and title stay below the bar, and on Android the bar's icons turn to read against the sheet where it is as wide as the window. Along the bottom edge and with fullScreen only. |
overscrollMovesSheet | Boolean | true | whether a drag down on content that is already at its top goes on to move the sheet down: to half open, then closed. Turn it off for a list that should only scroll; the handle and the title still drag the sheet, and a drag up on the content still opens a half-open sheet. Along the bottom edge only. |
scrollContent | Boolean | true | whether the sheet puts content in a scroll area under the title. Turn it off for content that scrolls itself, such as a LazyColumn, which cannot sit in a scroll area: content then gets the height left under the title, and its scrolling still opens a half-open sheet and moves the sheet down past its top. Give a LazyColumn Modifier.fillMaxWidth(): a list is only as wide as the rows on screen, and a drag beside them would not scroll it. |
style | SheetStyle? | null | this call's own style: over every enclosing one and under the density. Only the fields it sets change, on every platform. |
colors | SheetColors? | null | this call's own colours. Only the ones it sets change; the rest stay the theme's. |
tagKey | String? | null | a locale-independent key for automation: the sheet's tag is Sheet_<tagKey>, on the sheet as it is drawn, and the side sheet's close button is SheetClose_<tagKey>. |
content | @Composable ColumnScope.() -> Unit | what the sheet holds, under the title. |
SheetStyle
PantrixSheet's geometry.
| Parameter | Type | Default | What it is |
|---|---|---|---|
shape | ShapeSpec | ShapeSpec.Unspecified | The corners that face the app: the top two of a sheet along the bottom edge, the start two of one along the end edge. The corners at the window's edge are square. Large on Android and iOS, medium on desktop and the web. |
maxWidth | Dp | Dp.Unspecified | The widest a sheet along the bottom edge gets; in a wider window it stays in the middle of the edge. |
sideWidth | Dp | Dp.Unspecified | How wide a sheet along the end edge is, where the window leaves it that room. |
margin | Dp | Dp.Unspecified | Kept clear between the sheet and the far side of the window, so that the scrim still shows there and a tap on it closes the sheet: above a tall sheet along the bottom edge, beside a sheet along the end edge in a narrow window. It is a target for a tap, so it is each platform's smallest touch target. |
elevation | Dp | Dp.Unspecified | How high the sheet floats over the scrim, which is how large its shadow is. None on iOS. |
handleWidth | Dp | Dp.Unspecified | The drag handle a sheet along the bottom edge draws while a drag can move it. |
handleHeight | Dp | Dp.Unspecified | How thick the drag handle is: handleWidth is how long it is. |
handlePadding | Dp | Dp.Unspecified | Above and below the handle. |
SheetColors
What a PantrixSheet is painted with, for a call that does not take the theme's colours: a colour that is set is used, and one that is left out stays the theme's.
| Parameter | Type | Default | What it is |
|---|---|---|---|
container | Color | Color.Unspecified | The sheet's fill. The theme's surface. |
title | Color | Color.Unspecified | The title. The theme's textPrimary. |
handle | Color | Color.Unspecified | The drag handle of a sheet along the bottom edge, as it is drawn, with its own alpha. The theme's textSecondary at 40%: faint, and seen on any surface. |
closeIcon | Color | Color.Unspecified | The icon of the close button of a sheet along the end edge. The theme's onSurface. |
scrim | Color | Color.Unspecified | The dimming laid over the app behind the sheet, with its own alpha. The theme's scrim. |
Accessibility
- Keyboard and screen reader. When the sheet is opened with the keyboard, focus starts on the first control; when it is opened by touch, focus stays where it was.
titleis announced when the sheet opens and is the heading of what it holds. A screen reader cannot drag, so it is offered actions that open a half-open sheet fully and bring it back.