Back Button
A button that goes back to the screen before, for a PantrixTopBar's navigation icon: an icon button a screen reader calls "Back" in the theme's language.
import com.pantrixui.components.container.topbar.PantrixBackButton
- Title and subtitle.
titleis the screen's name, on one line;subtitleis written under it, smaller: a count, a state. Either is cut short when it does not fit the room between the navigation icon and the actions, and does not run under them. Their text styles are given withtitleStyleandsubtitleStyle; unless given, they are the theme'stitleandfootnotestyles. - Platform. The title stands at the start on Android, desktop and the web, and in the middle of the bar on iOS. A title in the middle is in the exact middle of the bar while it fits there; when it does not, it moves away from the wider side. The bar is 54dp tall on Android and iOS, 56dp on desktop and the web.
- Navigation icon and actions.
navigationIconstands before the title: a back button, or a button that opens a menu.actionsis a row after the title: icon buttons for what the screen can do, the most used first. - Back button.
PantrixBackButtondraws the platform's icon: an arrow on Android, desktop and the web, a chevron on iOS; both turn round in a right-to-left layout. A screen reader calls it "Back" in the theme's language. - Line over scrolled content. While
scrolledis true a line is drawn along the bar's bottom edge. The line is inside the bar's height; nothing moves as it comes and goes. - Collapsing title. A bar given a
statewrites its title large while it is open, in a band under the row; as the content scrolls up the band closes and the title comes into the row. The content hands the bar its scroll withModifier.topBarScroll(state). - Insets.
insetsis the room the bar keeps clear at its edges: for the status bar and a cutout. The bar's fill runs behind them. - Colour. The bar is the theme's
surface, with the title intextPrimaryand the subtitle intextSecondary.TopBarColorsgives the fill, the colour of the content and the line; on a bar with a fill, all of it is written in one colour. - Reading order. A screen reader and the Tab key go through the bar in the order it reads: the navigation icon, the title and the subtitle under it, the actions. The title is the screen's heading for a screen reader.
- Automation. With a
tagKeythe bar is taggedTopBar_<tagKey>, and the large title of a collapsing barTopBar_<tagKey>.Title_large. The back button is an icon button: it carries the tagIconButton_<tagKey>, and its press is reported as an icon button's.
Examples
Back button, actions and content scrolling under the bar
A conversation screen: a back button, two actions and a list that scrolls under the bar. navigationIcon puts a PantrixBackButton before the title; the subtitle says how many times the back button has been pressed. actions puts two icon buttons after the title: drawn as they are, in the brand's colour.
Scroll the list: as soon as it leaves its top, a line appears along the bar's bottom edge (scrolled), and it stays for as long as the list can scroll back. The line is inside the bar's height; nothing moves as it comes and goes.
val list = rememberLazyListState()
var back by rememberSaveable { mutableIntStateOf(0) }
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
Column(modifier = Modifier.height(220.dp)) {
PantrixTopBar(
title = "Conversation",
subtitle = if (back == 0) null else "Back presses: $back",
navigationIcon = { PantrixBackButton(onClick = { back++ }, tagKey = "back") },
actions = {
PantrixIconButton(
imageVector = PantrixIcons.Search,
contentDescription = "Search",
onClick = {},
tagKey = "search"
)
PantrixIconButton(
imageVector = PantrixIcons.MoreHorizontal,
contentDescription = "More",
onClick = {},
tagKey = "more"
)
},
scrolled = list.canScrollBackward,
tagKey = "conversation"
)
LazyColumn(
// The list is as wide as the bar: left narrower, a finger on the space beside it would scroll
// the page, not the list.
modifier = Modifier.fillMaxWidth(),
state = list,
contentPadding = PaddingValues(PantrixTheme.spacing.lg),
verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md)
) {
items(12) { index -> PantrixText(text = "Message ${index + 1}") }
}
}
}
A title that does not fit is cut short
The title does not run under the navigation icon or the actions: a title too long for the room left between them is cut short. The bar here is at most 360dp wide; the title does not fit between the back button and the star, and its end is cut off.
PantrixSurface(modifier = Modifier.widthIn(max = 360.dp), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Third-quarter reports, as they were sent to the board of directors",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "long_back") },
actions = {
PantrixIconButton(
imageVector = PantrixIcons.Star,
contentDescription = "Add to favourites",
onClick = {},
tagKey = "favourite"
)
},
tagKey = "long"
)
}
Where the title stands: at the start and in the middle
Where the title stands follows the platform: at the start on Android, desktop and the web, in the middle of the bar on iOS. Here the same bar is drawn twice, and the place is given with style: TopBarStyle(titleAlignment = ...). Both show, whichever platform you are on.
A title in the middle is in the middle of the bar whatever stands beside it: the back button does not push it aside.
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Settings",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "start_back") },
style = TopBarStyle(titleAlignment = TopBarTitleAlignment.Start),
tagKey = "start"
)
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Settings",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "center_back") },
style = TopBarStyle(titleAlignment = TopBarTitleAlignment.Center),
tagKey = "center"
)
}
The back button's icon
PantrixBackButton takes the icon it draws from the bar's style: TopBarStyle.backIcon. The platform's own icon is an arrow on Android, desktop and the web, a chevron on iOS. Here each of the two bars is given an icon with style; a back button in a bar's navigationIcon uses the style that bar was given.
Both turn round in a right-to-left layout: flip the reading direction from the "Typeface and direction" menu in the app's top bar and look.
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Arrow",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "arrow_back") },
style = TopBarStyle(backIcon = BackButtonIcon.Arrow),
tagKey = "arrow"
)
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Chevron",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "chevron_back") },
style = TopBarStyle(backIcon = BackButtonIcon.Chevron),
tagKey = "chevron"
)
}
With nowhere to go back to
On the first screen of a flow there is nowhere to go back to: the enabled of PantrixBackButton is false and the button cannot be pressed. Move on with the button at the end: the back button is enabled from the second step on.
var step by rememberSaveable { mutableIntStateOf(0) }
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Step ${step + 1} / 3",
navigationIcon = { PantrixBackButton(onClick = { step -= 1 }, enabled = step > 0, tagKey = "step_back") },
actions = {
PantrixIconButton(
imageVector = PantrixIcons.ArrowForward,
contentDescription = "Next step",
onClick = { step += 1 },
enabled = step < 2,
tagKey = "step_next"
)
},
tagKey = "steps"
)
}
A large title that collapses as the list scrolls
A collapsing title: the state is the app's and is kept with rememberPantrixTopBarState(); the list hands the bar its scroll with Modifier.topBarScroll(state). While the bar is open the title is written large, in the theme's headline style, in a band under the row. The large title stands at the bar's start on every platform; the title in the row stands where the platform has it, in the middle on iOS.
Scroll the list up: first the band closes and the title comes into the row, and only then does the list scroll. The bar opens again only once the list is back at its top. Left between its two heights by a finger, it goes on to the nearer; after a mouse wheel it goes on the way the wheel was last turned. A screen reader reads the title in the row: that one is there all the time, and the title is not read twice.
The bar and the list are inside a scaffold, as they would be in an app: the list fills the whole scaffold and is given the bar's height as its top inset. As the bar closes and opens the list itself stays where it is; only its inset changes. The button below takes the list back to its top and opens the bar by setting collapsedFraction to zero.
val topBar = rememberPantrixTopBarState()
val list = rememberLazyListState()
val scope = rememberCoroutineScope()
val spacing = PantrixTheme.spacing
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixScaffold(
modifier = Modifier.height(320.dp),
topBar = { insets ->
PantrixTopBar(
title = "Inbox",
subtitle = "3 unread",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "collapsing_back") },
actions = {
PantrixIconButton(
imageVector = PantrixIcons.Search,
contentDescription = "Search",
onClick = {},
tagKey = "collapsing_search"
)
},
scrolled = list.canScrollBackward,
state = topBar,
insets = insets,
tagKey = "collapsing"
)
},
containerColor = PantrixTheme.colors.surface,
// A box inside the page: the system bars are in the page's scaffold, and this one has no insets.
windowInsets = WindowInsets(0, 0, 0, 0)
) { padding ->
LazyColumn(
modifier = Modifier.fillMaxSize().topBarScroll(topBar),
state = list,
contentPadding = PaddingValues(
start = spacing.lg,
top = padding.calculateTopPadding() + spacing.lg,
end = spacing.lg,
bottom = spacing.lg
),
verticalArrangement = Arrangement.spacedBy(spacing.md)
) {
items(24) { index -> PantrixText(text = "Conversation ${index + 1}") }
}
}
}
PantrixButton(
text = "Back to top and open the bar",
onClick = {
scope.launch {
list.scrollToItem(0)
topBar.collapsedFraction = 0f
}
},
variant = ButtonVariant.Outline,
size = ButtonSize.Small,
tagKey = "collapsing_to_top"
)
The insets the bar keeps clear
insets is the room the bar keeps clear at its edges: room for the status bar and a cutout. A PantrixScaffold hands it to its top bar itself; here a top inset of 28dp is given by hand. The bar's fill runs behind the inset, and the title and the back button stay below it: the bar is as tall as its style's height plus the inset.
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Settings",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "insets_back") },
colors = TopBarColors(container = PantrixTheme.colors.primary.container),
insets = PaddingValues(top = 28.dp),
tagKey = "insets"
)
}
In its own colours
colors gives the bar colours of its own in place of the theme's. The first bar's fill is the colour of one of the theme's roles, and that alone is given: the theme pairs the colour of the title, the subtitle and the back button with it.
The second bar is in a colour the theme does not have, the app's own purple: the colour that reads on it is given with content. The title, the subtitle and the back button take the content colour. The icon button has colours of its own: the colour that reads on the fill is given to it separately.
val own = ownColors()
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Inbox",
subtitle = "Only the fill is given",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "role_back") },
colors = TopBarColors(container = PantrixTheme.colors.primary.color),
tagKey = "role"
)
}
PantrixSurface(modifier = Modifier.fillMaxWidth(), border = PantrixTheme.colors.divider) {
PantrixTopBar(
title = "Inbox",
subtitle = "3 unread",
navigationIcon = { PantrixBackButton(onClick = {}, tagKey = "own_back") },
actions = {
PantrixIconButton(
imageVector = PantrixIcons.Search,
contentDescription = "Search",
onClick = {},
colors = ButtonColors(container = Color.Transparent, content = own.onColor),
tagKey = "own_search"
)
},
colors = TopBarColors(container = own.color, content = own.onColor),
tagKey = "own"
)
}
API
onClick
A button that goes back to the screen before, for a PantrixTopBar's navigation icon: an icon button a screen reader calls "Back" in the theme's language.
| Parameter | Type | Default | What it is |
|---|---|---|---|
onClick | () -> Unit | called at a press: go back to the screen before. | |
modifier | Modifier | Modifier | the button's modifier. |
enabled | Boolean | true | whether the button can be pressed. |
tagKey | String? | null | the button's automation key, the same in every language. |