Scaffold
The layout of a screen: a top bar, a bottom bar and a side bar around the content, and the app's snackbars over the content.
import com.pantrixui.components.container.scaffold.PantrixScaffold
- Three bars.
topBar,bottomBarandsideBar. The side bar runs the scaffold's full height along the start edge, on the right in a right-to-left layout; the top and the bottom bar span the remaining width. - The content's padding. The content reaches under the bars. The
paddinggiven tocontentis, on each edge, the bar's size or the window inset of an edge without a bar, whichever is larger. It is passed to a list ascontentPadding: the list scrolls under the bars. - Window insets. Each slot is given, as
insets, the insets of the window edges it touches: a bar extends its fill behind the system bar and keeps its content in the safe area. Insets a parent has consumed are not given again. - Adaptive navigation. The second
PantrixScaffold, the one that takesnavigation, moves the navigation according to its own width: along the bottom edge while the scaffold is narrower than 600dp, and along the start edge from that width on. The threshold is changed withScaffoldStyle. - A foldable on a table. On a device held half open with its fold lying across, the navigation stays at the bottom whatever the width.
- The system's vertical bar. On the iPhone Duo's outer screen the system keeps a column along the right edge for bars that stand upright, with the status bar at its top. A scaffold that reaches that column puts the navigation at the column's bottom (
ScaffoldNavigationPlacement.VerticalBar): an uprightPantrixNavigationBaris drawn there. APantrixTopBarinside it hands its back button and its actions to the column's top too, under the status bar, and its title stays where it is. It does so only in a window that stands upright and has no fold: on the inner screen and on its side the status bar stays alone in the column, and the navigation and the buttons are where they always are. Of two scaffolds one inside the other, the outer one holds the column. - Snackbars. A scaffold given
snackbarsshows them over the content, between the bars, and provides them to everything inside it asLocalPantrixSnackbars. - Colour.
containerColoris drawn behind everything, the system bars included. On the theme's own grounds the scaffold provides no content colour; on a role's fill it provides the colour paired with that fill. - Order. A keyboard and a screen reader go from the side bar to the top bar, the buttons in the vertical bar, the content, the bottom bar and the snackbars. The bars and the snackbars are drawn over the content.
Examples
Top bar and the content's padding
A top bar and a list that scrolls under it. topBar is given insets and passes them on to the bar. The content fills the whole scaffold, the space under the bar too: the padding that comes to content was passed to the list as contentPadding, so the first row starts below the bar and the list passes under the bar as it scrolls. Once the list has left its start, the bar draws a line along its bottom edge (scrolled).
The scaffold is inside the page, so it was given a height and windowInsets was set to zero; on a screen neither is written.
val list = rememberLazyListState()
PantrixScaffold(
modifier = Modifier.height(320.dp).border(1.dp, PantrixTheme.colors.divider),
topBar = { insets ->
PantrixTopBar(
title = "Inbox",
scrolled = list.canScrollBackward,
insets = insets,
tagKey = "scaffold_inbox"
)
},
windowInsets = WindowInsets(0)
) { padding ->
LazyColumn(modifier = Modifier.fillMaxSize(), state = list, contentPadding = padding) {
items(20) { index ->
PantrixText(text = "Conversation ${index + 1}", modifier = Modifier.padding(PantrixTheme.spacing.lg))
}
}
}
Bottom bar
bottomBar spans the remaining width below the content: here it is a PantrixNavigationBar with three destinations. Which destination is open is the screen's state; the bar only reports the one that was pressed.
The content keeps clear of both bars with padding: the top padding is the height of the top bar, and the bottom padding that of the bottom bar. The line in the middle shows the values the scaffold gave.
var destination by rememberSaveable { mutableStateOf("inbox") }
PantrixScaffold(
modifier = Modifier.height(320.dp).border(1.dp, PantrixTheme.colors.divider),
topBar = { insets -> PantrixTopBar(title = "Mail", insets = insets) },
bottomBar = { insets ->
PantrixNavigationBar(
items = DESTINATIONS,
selected = destination,
onSelect = { destination = it },
insets = insets,
tagKey = "scaffold_bottom"
)
},
windowInsets = WindowInsets(0)
) { padding ->
Column(
modifier = Modifier.fillMaxSize().padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// A bar's height may be no whole number of dp on a screen: the nearest whole number is written.
val top = padding.calculateTopPadding().value.roundToInt()
val bottom = padding.calculateBottomPadding().value.roundToInt()
PantrixText(text = "Current destination: ${DESTINATIONS.first { it.key == destination }.label}")
PantrixText(text = "Top padding ${top}dp, bottom padding ${bottom}dp", color = PantrixTheme.colors.textSecondary)
}
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
Focus in a list that scrolls beneath the bars
While the content scrolls beneath the bars, what takes the focus by the keyboard does not stay behind a bar. Compose brings what takes the focus to the list's edge, and since the list reaches under the bars that edge is behind a bar. Here every item says how much the list keeps free at its ends (Modifier.scrollPadding): what takes the focus is brought inside that, between the two bars.
The padding is the share the scaffold gave and the room of a focus ring (PantrixTheme.platform.focusRingReach). In a column the ring asks for its own room; in a lazy list it cannot, so the list adds that room to its contentPadding and tells its items the same value.
Move down by pressing Tab on a keyboard and up by pressing Shift with Tab: every button is shown with its ring between the two bars.
var destination by rememberSaveable { mutableStateOf("inbox") }
val ring = PantrixTheme.platform.focusRingReach
PantrixScaffold(
modifier = Modifier.height(320.dp).border(1.dp, PantrixTheme.colors.divider),
topBar = { insets -> PantrixTopBar(title = "Labels", insets = insets) },
bottomBar = { insets ->
PantrixNavigationBar(
items = DESTINATIONS,
selected = destination,
onSelect = { destination = it },
insets = insets,
tagKey = "scaffold_focus"
)
},
windowInsets = WindowInsets(0)
) { padding ->
// The bars' share and, over it and under it, the room of a ring: the list's padding, and what its items tell of.
val room = padding + PaddingValues(vertical = ring)
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = room,
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
horizontalAlignment = Alignment.CenterHorizontally
) {
items(12) { index ->
PantrixButton(
text = "Label ${index + 1}",
onClick = {},
modifier = Modifier.scrollPadding(room),
variant = ButtonVariant.Outline,
tagKey = "scaffold_label_${index + 1}"
)
}
}
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
Side bar
sideBar runs the scaffold's full height along the start edge: here it is a PantrixNavigationRail. The top bar spans the width that is left beside it and does not reach over it. In a right-to-left layout the side bar is on the right.
A keyboard and a screen reader come to the side bar first, then to the top bar, then to the content.
var destination by rememberSaveable { mutableStateOf("inbox") }
PantrixScaffold(
modifier = Modifier.height(320.dp).border(1.dp, PantrixTheme.colors.divider),
topBar = { insets -> PantrixTopBar(title = "Mail", insets = insets) },
sideBar = { insets ->
PantrixNavigationRail(
items = DESTINATIONS,
selected = destination,
onSelect = { destination = it },
insets = insets,
tagKey = "scaffold_side"
)
},
windowInsets = WindowInsets(0)
) { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) {
PantrixText(text = "Current destination: ${DESTINATIONS.first { it.key == destination }.label}")
}
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
Navigation that moves with the width
The PantrixScaffold that takes navigation chooses the place of the navigation itself, and tells the navigation where it is (placement): along the bottom edge while the scaffold is narrower than 600dp, and along the start edge from that width on. The navigation draws the same destinations as a navigation bar at the bottom and as a rail along the start edge.
The width is the scaffold's own, not the window's: a scaffold in a box goes by the width of the box. If the box is narrower than 600dp the navigation is at the bottom; otherwise it is along the start edge. Pick "Narrow": the box becomes 360dp wide and the navigation moves down to the bottom. In a narrow window the box does not reach 600dp whichever is picked, and the navigation stays at the bottom.
The content stays in the same composition while the navigation moves: press the counter, then change the width; the number is still there.
var wide by rememberSaveable { mutableStateOf(true) }
var destination by rememberSaveable { mutableStateOf("inbox") }
PantrixSegmentButton(
options = listOf(
SegmentOption(value = false, label = "Narrow", tagKey = "narrow"),
SegmentOption(value = true, label = "Wide", tagKey = "wide")
),
selected = wide,
onSelect = { wide = it },
fullWidth = false,
tagKey = "scaffold_width"
)
val width = if (wide) Modifier.fillMaxWidth() else Modifier.width(360.dp)
PantrixScaffold(
navigation = { placement, insets ->
ScaffoldDestinations(placement, insets, selected = destination, tagKey = "scaffold_adaptive") {
destination = it
}
},
modifier = width.height(320.dp).border(1.dp, PantrixTheme.colors.divider),
topBar = { insets -> PantrixTopBar(title = "Mail", insets = insets) },
windowInsets = WindowInsets(0)
) { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) {
var count by remember { mutableIntStateOf(0) }
PantrixButton(
text = "Counter: $count",
onClick = { count += 1 },
variant = ButtonVariant.Soft,
tagKey = "scaffold_count"
)
}
}
@Composable
private fun ScaffoldDestinations(
placement: ScaffoldNavigationPlacement,
insets: PaddingValues,
selected: String,
tagKey: String,
onSelect: (String) -> Unit
) {
when (placement) {
ScaffoldNavigationPlacement.Bottom -> {
PantrixNavigationBar(
items = DESTINATIONS,
selected = selected,
onSelect = onSelect,
insets = insets,
tagKey = tagKey
)
}
ScaffoldNavigationPlacement.Start -> {
PantrixNavigationRail(
items = DESTINATIONS,
selected = selected,
onSelect = onSelect,
insets = insets,
tagKey = tagKey
)
}
ScaffoldNavigationPlacement.VerticalBar -> {
PantrixNavigationBar(
items = DESTINATIONS,
selected = selected,
onSelect = onSelect,
orientation = NavigationBarOrientation.Vertical,
tagKey = tagKey
)
}
}
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
The width at which the navigation moves
The width at which the navigation moves to the start edge is in the scaffold's style: ScaffoldStyle.sideNavigationMinWidth, 600dp on every platform. This scaffold was given 280dp through style: if the box is 280dp or wider the navigation is along the start edge, even in a window narrower than 600dp.
var destination by rememberSaveable { mutableStateOf("inbox") }
PantrixScaffold(
navigation = { placement, insets ->
ScaffoldDestinations(placement, insets, selected = destination, tagKey = "scaffold_threshold") {
destination = it
}
},
modifier = Modifier.height(320.dp).border(1.dp, PantrixTheme.colors.divider),
windowInsets = WindowInsets(0),
style = ScaffoldStyle(sideNavigationMinWidth = 280.dp)
) { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) {
PantrixText(text = "Current destination: ${DESTINATIONS.first { it.key == destination }.label}")
}
}
@Composable
private fun ScaffoldDestinations(
placement: ScaffoldNavigationPlacement,
insets: PaddingValues,
selected: String,
tagKey: String,
onSelect: (String) -> Unit
) {
when (placement) {
ScaffoldNavigationPlacement.Bottom -> {
PantrixNavigationBar(
items = DESTINATIONS,
selected = selected,
onSelect = onSelect,
insets = insets,
tagKey = tagKey
)
}
ScaffoldNavigationPlacement.Start -> {
PantrixNavigationRail(
items = DESTINATIONS,
selected = selected,
onSelect = onSelect,
insets = insets,
tagKey = tagKey
)
}
ScaffoldNavigationPlacement.VerticalBar -> {
PantrixNavigationBar(
items = DESTINATIONS,
selected = selected,
onSelect = onSelect,
orientation = NavigationBarOrientation.Vertical,
tagKey = tagKey
)
}
}
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
In the system's vertical bar
On the iPhone Duo's outer screen, from iOS 27.1 on, the system keeps a column along the right edge for bars that stand upright: the status bar at its top and the app's bars below it. Pick "Cover": the faint strip is where the status bar is. The scaffold reaches that column, so it puts the navigation at the column's bottom and tells it ScaffoldNavigationPlacement.VerticalBar; an upright PantrixNavigationBar is drawn there. The PantrixTopBar in the content hands its back button and its two actions to the column's top, under the status bar, and its title stays where it is. The app's own shell stands the same way on the Duo's outer screen.
In the upright bar the destinations have their icons alone, as UIKit's tabs in that column: rest on an icon or press it long, and its name shows in a tooltip. Turn "Labels" on: the names are written under the icons too (showLabels). The other windows have no column: the navigation is at the bottom or at the start edge by the width, and the buttons are in the top bar. On the Duo's inner screen and on its side the system keeps the column too, but the scaffold leaves it to the status bar, and this is how it stays.
The scaffold is as tall as the outer screen, 678dp; the place in the column is measured by the window, and the scaffold starts at the window's top.
var labels by rememberSaveable { mutableStateOf(false) }
var destination by rememberSaveable { mutableStateOf("inbox") }
PantrixScaffold(
navigation = { placement, insets ->
when (placement) {
ScaffoldNavigationPlacement.Bottom -> PantrixNavigationBar(
items = DESTINATIONS,
selected = destination,
onSelect = { destination = it },
insets = insets,
tagKey = "scaffold_upright"
)
ScaffoldNavigationPlacement.VerticalBar -> PantrixNavigationBar(
items = DESTINATIONS,
selected = destination,
onSelect = { destination = it },
orientation = NavigationBarOrientation.Vertical,
showLabels = labels,
tagKey = "scaffold_upright"
)
ScaffoldNavigationPlacement.Start -> PantrixNavigationRail(
items = DESTINATIONS,
selected = destination,
onSelect = { destination = it },
insets = insets,
tagKey = "scaffold_upright"
)
}
},
modifier = Modifier.fillMaxWidth().height(678.dp),
windowInsets = WindowInsets(0)
) { padding ->
Column(modifier = Modifier.fillMaxSize()) {
// The bar has the scaffold's whole width, as UIKit's top bar does; the content keeps clear of the column by its padding.
PantrixTopBar(
title = DESTINATIONS.first { it.key == destination }.label,
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "scaffold_upright_back") },
actions = {
PantrixIconButton(
imageVector = PantrixIcons.Search,
contentDescription = "Search",
onClick = {},
tagKey = "scaffold_upright_search"
)
PantrixIconButton(
imageVector = PantrixIcons.MoreHorizontal,
contentDescription = "More",
onClick = {},
tagKey = "scaffold_upright_more"
)
},
tagKey = "scaffold_upright"
)
PantrixSwitchButton(
checked = labels,
onCheckedChange = { labels = it },
modifier = Modifier.padding(padding).padding(PantrixTheme.spacing.lg),
label = "Labels",
tagKey = "scaffold_upright_labels"
)
}
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
Snackbars
A scaffold given snackbars shows them over the content and inside the padding it gives the content: between the bars. It provides the same state to everything inside it as LocalPantrixSnackbars; a screen does not take the state as a parameter to show a snackbar. The button here is a separate composable inside the scaffold, and reads the state from there.
Press "Archive": the snackbar appears inside the scaffold in this box, not in the app's window. Its place depends on the platform: in the middle of the bottom edge on Android, at the top on iOS, and in the bottom end corner on desktop and the web. A scaffold given no snackbars keeps the LocalPantrixSnackbars of the layout around it: the boxes in the other examples see the app's.
val snackbars = rememberPantrixSnackbarState()
PantrixScaffold(
modifier = Modifier.height(320.dp).border(1.dp, PantrixTheme.colors.divider),
topBar = { insets -> PantrixTopBar(title = "Inbox", insets = insets) },
bottomBar = { insets ->
PantrixNavigationBar(
items = DESTINATIONS,
selected = "inbox",
onSelect = {},
insets = insets,
tagKey = "scaffold_snackbars"
)
},
snackbars = snackbars,
windowInsets = WindowInsets(0)
) { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) {
ScaffoldArchiveButton()
}
}
@Composable
private fun ScaffoldArchiveButton() {
val snackbars = LocalPantrixSnackbars.current
val scope = rememberCoroutineScope()
PantrixButton(
text = "Archive",
onClick = { scope.launch { snackbars?.show(message = "Conversation archived", tagKey = "scaffold_archived") } },
tagKey = "scaffold_archive"
)
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
The window's insets
In a window windowInsets is not written: the scaffold reads the insets of the system bars and the display cutout itself. This box has no system bar, so two insets were given by hand: 28dp at the top and 20dp at the bottom; a caption stands where each bar would be.
Each bar is given the inset of the edge it touches as insets. The top bar and the bottom bar pad their content by it: their fill reaches behind the inset, and the title and the destinations stay clear of it. The content's padding grows to match: the values in the line in the middle are the bar's height plus the window's inset.
val caption = PantrixTheme.typography.caption
val quiet = PantrixTheme.colors.textSecondary
Box(modifier = Modifier.height(320.dp).border(1.dp, PantrixTheme.colors.divider)) {
PantrixScaffold(
topBar = { insets -> PantrixTopBar(title = "Settings", insets = insets) },
bottomBar = { insets ->
PantrixNavigationBar(
items = DESTINATIONS,
selected = "inbox",
onSelect = {},
insets = insets,
tagKey = "scaffold_insets"
)
},
windowInsets = WindowInsets(top = 28.dp, bottom = 20.dp)
) { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) {
val top = padding.calculateTopPadding().value.roundToInt()
val bottom = padding.calculateBottomPadding().value.roundToInt()
PantrixText(text = "Top padding ${top}dp, bottom padding ${bottom}dp")
}
}
PantrixText(
text = "where the status bar would be",
modifier = Modifier.align(Alignment.TopCenter).padding(top = 6.dp),
style = caption,
color = quiet
)
PantrixText(
text = "where the system bar would be",
modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 2.dp),
style = caption,
color = quiet
)
}
private val DESTINATIONS = listOf(
NavigationItem(key = "inbox", label = "Messages", icon = PantrixIcons.Inbox),
NavigationItem(key = "starred", label = "Starred", icon = PantrixIcons.Star),
NavigationItem(key = "people", label = "People", icon = PantrixIcons.Person)
)
Ground and content colour
containerColor is the scaffold's ground; its default is the theme's background. On the theme's own grounds the scaffold provides no content colour: each component draws in its own default. The upper scaffold was painted with a role's fill, and itself gave the text inside it the colour paired with that fill.
The colour of the lower one is not in the theme but is the app's own purple: the theme cannot know what reads on it, so that is given with contentColor.
val own = ownColors()
PantrixScaffold(
modifier = Modifier.height(96.dp),
containerColor = PantrixTheme.colors.primary.container,
windowInsets = WindowInsets(0)
) { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) {
PantrixText(text = "A role's fill: the scaffold gave the content colour")
}
}
PantrixScaffold(
modifier = Modifier.height(96.dp),
containerColor = own.color,
contentColor = own.onColor,
windowInsets = WindowInsets(0)
) { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) {
PantrixText(text = "A colour the theme does not have: contentColor was given")
}
}
API
modifier
The layout of a screen: a top bar, a bottom bar and a side bar around the content, and the app's snackbars over the content.
| Parameter | Type | Default | What it is |
|---|---|---|---|
modifier | Modifier | Modifier | the scaffold's modifier. The scaffold fills the space it is given. |
topBar | (@Composable (insets: PaddingValues) -> Unit)? | null | the bar above the content, given the insets of the edges it touches. |
bottomBar | (@Composable (insets: PaddingValues) -> Unit)? | null | the bar below the content, given the insets of the edges it touches. |
sideBar | (@Composable (insets: PaddingValues) -> Unit)? | null | the bar along the start edge, the scaffold's full height, given the insets of the edges it touches. |
snackbars | PantrixSnackbarState? | null | the app's snackbars: shown over the content, and provided to everything inside as LocalPantrixSnackbars. Without them, a scaffold shows no snackbars of its own and keeps the LocalPantrixSnackbars of the layout around it. |
snackbarHost | @Composable () -> Unit | { if (snackbars != null) PantrixSnackbarHost(snackbars) } | what shows the snackbars over the content: by default a PantrixSnackbarHost of snackbars. |
containerColor | Color | PantrixTheme.colors.background | drawn behind everything, the window's system bars included. |
contentColor | Color | Color.Unspecified | the colour of text and icons that set none. By default the one paired with containerColor when that is a role's fill, and none on the theme's background, on its surface or on a colour the theme does not know: pass one with such a colour when the components' own defaults do not read on it. |
windowInsets | WindowInsets | WindowInsets.systemBars.union(WindowInsets.displayCutout) | the insets the scaffold hands to its slots and keeps the content and the snackbars clear of, by default the system bars and the display cutout; any a parent has already consumed are left out. The keyboard is not among them: a screen with text fields adds Modifier.consumeWindowInsets(padding).imePadding() to its content. |
content | @Composable (padding: PaddingValues) -> Unit | the screen, given the padding that keeps it clear of the bars and the insets. |
navigation
The layout of a screen whose navigation moves with its width: along the bottom edge while the scaffold is narrower than ScaffoldStyle.sideNavigationMinWidth, 600dp by default, and along the start edge from that width on.
| Parameter | Type | Default | What it is |
|---|---|---|---|
navigation | @Composable (placement: ScaffoldNavigationPlacement, insets: PaddingValues) -> Unit | the app's destinations, given where they are and the insets of the edges they touch there. | |
modifier | Modifier | Modifier | the scaffold's modifier. The scaffold fills the space it is given. |
topBar | (@Composable (insets: PaddingValues) -> Unit)? | null | the bar above the content, given the insets of the edges it touches. |
snackbars | PantrixSnackbarState? | null | the app's snackbars: shown over the content, and provided to everything inside as LocalPantrixSnackbars. Without them, a scaffold shows no snackbars of its own and keeps the LocalPantrixSnackbars of the layout around it. |
snackbarHost | @Composable () -> Unit | { if (snackbars != null) PantrixSnackbarHost(snackbars) } | what shows the snackbars over the content: by default a PantrixSnackbarHost of snackbars. |
containerColor | Color | PantrixTheme.colors.background | drawn behind everything, the window's system bars included. |
contentColor | Color | Color.Unspecified | the colour of text and icons that set none. By default the one paired with containerColor when that is a role's fill, and none on the theme's background, on its surface or on a colour the theme does not know. |
windowInsets | WindowInsets | WindowInsets.systemBars.union(WindowInsets.displayCutout) | the insets the scaffold hands to its slots and keeps the content and the snackbars clear of, by default the system bars and the display cutout, without the keyboard; any a parent has already consumed are left out. |
style | ScaffoldStyle? | null | this call's own style, which says from what width the navigation moves to the start edge: over every enclosing one. Only the fields it sets change, on every platform. |
content | @Composable (padding: PaddingValues) -> Unit | the screen, given the padding that keeps it clear of the bars and the insets. |
ScaffoldStyle
PantrixScaffold's one decision of its own: how wide it has to be before its adaptive navigation moves from the bottom edge to the start edge.
| Parameter | Type | Default | What it is |
|---|---|---|---|
sideNavigationMinWidth | Dp | Dp.Unspecified | The narrowest a scaffold is when its adaptive navigation sits along the start edge; a narrower scaffold puts it along the bottom edge. 600dp on every platform, the width at which Material 3 moves navigation from a bar to a rail. |
Accessibility
- Keyboard.
windowInsetsis the system bars and the display cutout, and the keyboard is not among them: a screen with text fields addsModifier.consumeWindowInsets(padding).imePadding()to its content. - Focus. In content that scrolls beneath a bar, what takes the focus is brought to the edge of the scrolling area, and that edge is behind the bar. The content says what it keeps free at its ends with
Modifier.scrollPadding: the focus stays between the bars.