Page Scaffold
A screen inside an app's shell: its title and what it can do at its top, and under them what it shows, kept clear of the shell's bars, of the keyboard and of the window's edges. An app lays its shell out once with a PantrixScaffold, its navigation along the bottom of a narrow window and down the side of a wide one, and each of its screens with this.
import com.pantrixui.components.container.page.PantrixPageScaffold
- Bar. The page draws its own
PantrixTopBar, the scaffold none: as the page changes, the title goes with it. The bar holdstitle,subtitle, a back button whereonBackis given, and the actions. The bar's title starts where the content does, at the page's margin. When the content scrolls under the bar, a line is drawn along the bar's bottom edge. - The title in a wide page. With
titlePlacement = PageTitlePlacement.Adaptivethere is no bar from Expanded on: the title is written large at the top of the content, the subtitle under it, and the actions beside it are buttons with their labels. In a narrower page they are in the bar. - Large title. With
largeTitlethe title is written large under the bar, and goes up into the bar as the content scrolls; the same on every platform. - Under the title.
headerstands under the title and scrolls with the content: a description, a notice.stickyHeaderstays under the bar: tabs, a search, filters. The line is under it then. - Actions.
actionsare laid out by the room: buttons with their labels in a wide page, the primary one filled; icons in a Compact one, their names in a tooltip and for a screen reader. What has no room, and in a Compact page what has no icon, is in the "…" menu, in the order given.onActionis told the key of the one pressed.trailingstands after them all the time: the app's own buttons. - Scroll. With
PageScroll.Pagethe title,headerand the content scroll together. WithPageScroll.Nonethe content fills the room under the bar and scrolls itself, and takes the room it is to leave withcontentPadding. A list's page is aPantrixLazyPageScaffold: its title and itsheaderare the list's first rows. - Width. By default (
PageWidth.Full) the page takes its whole width, in a wide window too: cards, a table, a dashboard.PageWidth.Contentkeeps a page that is read to the theme's content width, in line with the title. - The window's title. The page names the tab on the web, the window on the desktop, after itself (
PantrixWindowTitle); the app adds its own name withLocalPantrixWindowTitleFormat. A page inside a page, as the examples on this page are, leaves the title to the outer one.
Examples
A page of an admin
The orders page of an admin panel. titlePlacement = PageTitlePlacement.Adaptive: choose "Wide" in the card's picker, and the title and the actions go down to the top of the content and the bar goes; in "Narrow" they are in the bar and the actions turn into icons. "Archive" has no icon: in a narrow page it is in the "…" menu. The key of the action pressed is written in the page.
var pressed by rememberSaveable { mutableStateOf<String?>(null) }
PageExampleFrame {
PantrixPageScaffold(
title = "Orders",
subtitle = "12 open",
actions = listOf(
PageAction(key = "new", label = "New order", icon = PantrixIcons.Plus, primary = true),
PageAction(key = "export", label = "Export", icon = PantrixIcons.Download),
PageAction(key = "archive", label = "Archive")
),
onAction = { pressed = it },
header = {
PantrixText("Orders of the last 30 days", color = PantrixTheme.colors.textSecondary)
},
titlePlacement = PageTitlePlacement.Adaptive,
tagKey = "orders"
) {
PantrixText(pressed?.let { "Last pressed: $it" } ?: "No action pressed yet")
repeat(5) { index ->
PantrixCard(modifier = Modifier.fillMaxWidth().padding(top = 8.dp)) {
PantrixText("Order #${1040 + index}", modifier = Modifier.padding(16.dp))
}
}
}
}
@Composable
private fun PageExampleFrame(content: @Composable () -> Unit) {
PantrixSurface(
modifier = Modifier.fillMaxWidth().height(PAGE_EXAMPLE_HEIGHT),
border = PantrixTheme.colors.divider
) {
Box { content() }
}
}
private val PAGE_EXAMPLE_HEIGHT = 420.dp
Tabs under the bar, a list that scrolls itself
The tabs stand under the bar (stickyHeader), the list scrolls itself (scroll = PageScroll.None) and takes the room the page leaves with contentPadding: its last row inside the room, the margin inside the rows. Scroll the list: the line shows under the tabs. Press the back button and the subtitle counts.
var tab by rememberSaveable { mutableIntStateOf(0) }
var backs by rememberSaveable { mutableIntStateOf(0) }
val tabs = listOf(TabItem("All"), TabItem("On sale"), TabItem("Sold out"))
PageExampleFrame {
PantrixPageScaffold(
title = "Products",
subtitle = if (backs == 0) null else "Back presses: $backs",
onBack = { backs++ },
stickyHeader = { PantrixTabs(tabs = tabs, selectedIndex = tab, onSelect = { tab = it }, tagKey = "stock") },
scroll = PageScroll.None,
tagKey = "products"
) {
LazyColumn(contentPadding = contentPadding, verticalArrangement = Arrangement.spacedBy(12.dp)) {
items(List(20) { "${tabs[tab].label}: product ${it + 1}" }) { product -> PantrixText(product) }
}
}
}
@Composable
private fun PageExampleFrame(content: @Composable () -> Unit) {
PantrixSurface(
modifier = Modifier.fillMaxWidth().height(PAGE_EXAMPLE_HEIGHT),
border = PantrixTheme.colors.divider
) {
Box { content() }
}
}
private val PAGE_EXAMPLE_HEIGHT = 420.dp
A large title and a list
A list's page: PantrixLazyPageScaffold. Its rows are composed as they come into view; header is the list's first row. largeTitle = true: the title is written large under the bar, and goes up into the bar as the list scrolls. trailing puts the app's own button at the end of the bar. width = PageWidth.Content: a list that is read keeps to the theme's content width in a wide window.
PageExampleFrame {
PantrixLazyPageScaffold(
title = "Inbox",
trailing = {
PantrixIconButton(PantrixIcons.Search, contentDescription = "Search", onClick = {}, tagKey = "search")
},
header = { PantrixText("4 unread messages", color = PantrixTheme.colors.textSecondary) },
largeTitle = true,
width = PageWidth.Content,
verticalArrangement = Arrangement.spacedBy(12.dp),
tagKey = "inbox"
) {
items(List(30) { "Message ${it + 1}" }) { message -> PantrixText(message) }
}
}
@Composable
private fun PageExampleFrame(content: @Composable () -> Unit) {
PantrixSurface(
modifier = Modifier.fillMaxWidth().height(PAGE_EXAMPLE_HEIGHT),
border = PantrixTheme.colors.divider
) {
Box { content() }
}
}
private val PAGE_EXAMPLE_HEIGHT = 420.dp
Inside a shell, with measures of its own
A page inside a shell: the PantrixScaffold has a bottom bar, the page reads its room itself and leaves its last row above the bar. padding is that even when it is not given (LocalPantrixScaffoldPadding.current); it is written out here. style changes the page's margin and the room between its sections.
PageExampleFrame {
PantrixScaffold(
bottomBar = { insets ->
PantrixSurface(modifier = Modifier.fillMaxWidth().padding(insets).height(56.dp)) {
PantrixText("Bottom bar", modifier = Modifier.align(Alignment.Center))
}
}
) {
PantrixPageScaffold(
title = "Settings",
header = { PantrixText("A margin of 24 dp, 8 dp between the sections") },
padding = LocalPantrixScaffoldPadding.current,
style = PageScaffoldStyle(horizontalPadding = 24.dp, sectionGap = 8.dp),
tagKey = "settings"
) {
repeat(12) { index -> PantrixText("Setting ${index + 1}", modifier = Modifier.padding(vertical = 8.dp)) }
}
}
}
@Composable
private fun PageExampleFrame(content: @Composable () -> Unit) {
PantrixSurface(
modifier = Modifier.fillMaxWidth().height(PAGE_EXAMPLE_HEIGHT),
border = PantrixTheme.colors.divider
) {
Box { content() }
}
}
private val PAGE_EXAMPLE_HEIGHT = 420.dp
The window's title
The window's title is given without a page too: PantrixWindowTitle says the screen's name, and LocalPantrixWindowTitleFormat how it is written as the window's. Turn the switch on: on the web the tab's title, on the desktop the window's, becomes "Reports — Admin"; turn it off and this page's comes back. Android and iOS show their user no window's title.
var named by rememberSaveable { mutableStateOf(false) }
PantrixSwitchButton(
checked = named,
onCheckedChange = { named = it },
label = "Name the window \"Reports\"",
tagKey = "window_title"
)
if (named) {
CompositionLocalProvider(LocalPantrixWindowTitleFormat provides PantrixWindowTitleFormat { "$it — Admin" }) {
PantrixWindowTitle("Reports")
}
}
API
title
A screen inside an app's shell: its title and what it can do at its top, and under them what it shows, kept clear of the shell's bars, of the keyboard and of the window's edges. An app lays its shell out once with a PantrixScaffold, its navigation along the bottom of a narrow window and down the side of a wide one, and each of its screens with this.
| Parameter | Type | Default | What it is |
|---|---|---|---|
title | String | what the page is called: in its bar or its heading, its name for a screen reader, and the window's. | |
modifier | Modifier | Modifier | the page's modifier. |
subtitle | String? | null | a line under the title, a count or a state, or null for none. |
onBack | (() -> Unit)? | null | where the page goes back to, as its bar's back button; null where there is nowhere to go back to. The system's back, a gesture or a key, is the app's navigation's to hear. |
actions | List<PageAction> | emptyList() | what the page can do, the most used first: laid out by the room, the rest in a menu. Each has a key of its own among them. |
onAction | (key: String) -> Unit | {} | told the key of the action pressed. |
trailing | (@Composable RowScope.() -> Unit)? | null | what stands after the actions all the time, in a row: the app's own buttons. |
header | (@Composable () -> Unit)? | null | what stands under the title and over the content, and scrolls with them. |
stickyHeader | (@Composable () -> Unit)? | null | what stands under the bar and stays there as the page scrolls. |
titlePlacement | PageTitlePlacement | PageTitlePlacement.Bar | whether the title is in the bar at every width, or in the content of a wide page. |
largeTitle | Boolean | false | whether the bar's title stands in a large type under it, and goes up into it as the page scrolls. |
width | PageWidth | PageWidth.Full | how wide what the page shows is: the page's, or the theme's content width. |
scroll | PageScroll | PageScroll.Page | whether the page scrolls, or its content scrolls itself. |
padding | PaddingValues | LocalPantrixScaffoldPadding.current | what the page keeps clear of at its edges: the enclosing scaffold's padding by default. |
style | PageScaffoldStyle? | null | the page's geometry, over the theme's. |
tagKey | String? | null | the page's automation key, the same in every language: what the page and its parts are tagged by, the TestScope of what it holds, and the page its show event is of. |
content | @Composable PageScaffoldScope.() -> Unit | what the page shows, in a column; with PageScroll.None it is given the room to keep as PageScaffoldScope.contentPadding. |
PageScaffoldStyle
PantrixPageScaffold's geometry: the room around what a page holds and between its parts.
| Parameter | Type | Default | What it is |
|---|---|---|---|
horizontalPadding | Dp | Dp.Unspecified | The room between the page's sides and what it holds, past the scaffold's padding: unspecified, the theme's page margin, unless it is set. |
verticalPadding | Dp | Dp.Unspecified | The room above what the page holds and below it: 16dp on Android and iOS, 24dp on desktop and the web. |
sectionGap | Dp | Dp.Unspecified | The room between the page's heading, its header and its content: 16dp on Android and iOS, 24dp on desktop and the web. |
actionGap | Dp | Dp.Unspecified | The room between two of the page's actions, and between them and what stands beside them: 8dp. |
topBar | TopBarStyle? | null | The page's bar's own geometry, over the theme's. The bar's title starts at horizontalPadding unless this sets where. |
Accessibility
- Room and keyboard. The page reads the room of the
PantrixScaffoldaround it itself (LocalPantrixScaffoldPadding) and hands none of it on: a page inside a page does not leave the same room again. The page ends above the keyboard; a field that takes the focus is not left behind a bottom bar. - Screen reader, event and tags. The title is the page's heading and the pane's name. A page given a
tagKeyreports ashowevent each time it opens, with its tag, not its title; what is put inside it is inTestScope(tagKey).