diff --git a/components/src/main/kotlin/de/jeanlucmakiola/floret/components/Onboarding.kt b/components/src/main/kotlin/de/jeanlucmakiola/floret/components/Onboarding.kt index f8eb277..7e72158 100644 --- a/components/src/main/kotlin/de/jeanlucmakiola/floret/components/Onboarding.kt +++ b/components/src/main/kotlin/de/jeanlucmakiola/floret/components/Onboarding.kt @@ -33,6 +33,9 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +/** Standard touch target, reserved for the shell's navigation slot. */ +private val NavigationSlotSize = 48.dp + /** MD3 8dp spacing scale shared by the onboarding screens. */ object OnboardingSpace { val xs = 8.dp @@ -50,8 +53,14 @@ object OnboardingSpace { * as one family while keeping their own mark. * * A multi-step flow passes [progress] (see [OnboardingProgress]), which is - * pinned above the scrolling content. [topSpacing] gives a step whose hero is a - * large picture rather than a mark room to breathe without overflowing. + * pinned above the scrolling content, and [navigationIcon] for a step that can + * be stepped back from. The navigation slot's width is reserved whether or not + * it is filled, and mirrored on the other side, so the progress stays centred + * and does not shift as steps come and go. + * + * [topSpacing] gives a step whose hero is a large picture rather than a mark + * room to breathe. [contentPadding] is the scrolling column's own horizontal + * inset — a step built from grouped rows passes 0, since those carry their own. */ @Composable fun OnboardingScaffold( @@ -59,21 +68,26 @@ fun OnboardingScaffold( actions: @Composable ColumnScope.() -> Unit, modifier: Modifier = Modifier, progress: (@Composable () -> Unit)? = null, + navigationIcon: (@Composable () -> Unit)? = null, topSpacing: Dp = OnboardingSpace.xl, + contentPadding: Dp = OnboardingSpace.md, body: @Composable ColumnScope.() -> Unit, ) { Scaffold( modifier = modifier, containerColor = MaterialTheme.colorScheme.surface, topBar = { - if (progress != null) { - Box( + if (progress != null || navigationIcon != null) { + Row( modifier = Modifier .fillMaxWidth() .statusBarsPadding() - .padding(horizontal = OnboardingSpace.md, vertical = OnboardingSpace.sm), + .padding(horizontal = OnboardingSpace.xs, vertical = OnboardingSpace.sm), + verticalAlignment = Alignment.CenterVertically, ) { - progress() + Box(Modifier.size(NavigationSlotSize)) { navigationIcon?.invoke() } + Box(Modifier.weight(1f)) { progress?.invoke() } + Spacer(Modifier.width(NavigationSlotSize)) } } }, @@ -94,7 +108,7 @@ fun OnboardingScaffold( .fillMaxSize() .padding(innerPadding) .verticalScroll(rememberScrollState()) - .padding(horizontal = OnboardingSpace.md), + .padding(horizontal = contentPadding), horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(Modifier.height(topSpacing))