feat(components): a named header above a run of grouped rows

A settings page with a dozen categories in one flat run has no way to say
which of them are about how the app behaves and which are about the app
itself. GroupedSectionHeader is that label: it starts on GroupedListInset,
the edge the cards it names start on, and owns the gap to the group above,
so a page writes header-then-rows and nothing else.

primary rather than onSurfaceVariant — after the Android-15 settings
pattern Position already follows, and because a header that matches the
summaries underneath it stops separating anything.
This commit is contained in:
2026-09-09 15:17:56 +02:00
parent d7b7a542cd
commit 1af24841ae
2 changed files with 34 additions and 0 deletions
+7
View File
@@ -12,6 +12,13 @@ are no binary releases; "version" tracks the shared `version` in the root build
### Added
- **`components`** — `GroupedSectionHeader`, the label above a run of
`GroupedRow`s. Starts on `GroupedListInset` and owns the gap to the group
above, so a page writes header-then-rows and nothing else; `primary`, after
the Android-15 settings pattern the row system already follows. Wanted by
Agendula's settings hub, which had grown a flat run of a dozen rows with
nothing separating how the app behaves from what it is.
- **`components`** — `GroupedRow` takes a `contentColor`, the pair to the
existing `container`: a row given a tint now names the `on*` role that goes
with it instead of leaving its text on `onSurface`, which only happens to
@@ -53,6 +53,33 @@ fun positionOf(index: Int, count: Int): Position = when {
*/
val GroupedListInset = 16.dp
/**
* The label above a run of [GroupedRow]s, for a page whose groups are named
* rather than merely spaced apart — a settings hub that separates how the app
* behaves from what it is.
*
* Starts on [GroupedListInset], the same edge as the cards it names, and owns
* the space that separates its group from the one above, so a caller writes
* header-then-rows and nothing else. Coloured `primary` after the Android-15
* settings pattern this row system already follows: the headers are what makes
* the grouping readable at a glance, and `onSurfaceVariant` sinks them into the
* summaries.
*/
@Composable
fun GroupedSectionHeader(text: String, modifier: Modifier = Modifier) {
Text(
text = text,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary,
modifier = modifier.padding(
start = GroupedListInset,
end = GroupedListInset,
top = 24.dp,
bottom = 8.dp,
),
)
}
/** Corner radii for a grouped segment: full at the group's outer edges, small between. */
fun groupedShape(position: Position, full: Dp, small: Dp): Shape = when (position) {
Position.Alone -> RoundedCornerShape(full)