From a7cda2b3542efd5ecfcd0fbd6aba6fae3eb5eb6f Mon Sep 17 00:00:00 2001 From: Flaringapp Date: Tue, 7 Apr 2026 18:22:33 +0300 Subject: [PATCH 1/5] Define pin modifier --- .../api/ComposeCollapsingTopBar.klib.api | 1 + .../compose/topbar/CollapsingTopBar.kt | 33 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api b/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api index 147300a..65719c2 100644 --- a/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api +++ b/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api @@ -60,6 +60,7 @@ abstract interface com.flaringapp.compose.topbar/CollapsingTopBarScope { // com. abstract fun (androidx.compose.ui/Modifier).floating(): androidx.compose.ui/Modifier // com.flaringapp.compose.topbar/CollapsingTopBarScope.floating|floating@androidx.compose.ui.Modifier(){}[0] abstract fun (androidx.compose.ui/Modifier).nestedCollapse(com.flaringapp.compose.topbar.nestedcollapse/CollapsingTopBarNestedCollapseElement): androidx.compose.ui/Modifier // com.flaringapp.compose.topbar/CollapsingTopBarScope.nestedCollapse|nestedCollapse@androidx.compose.ui.Modifier(com.flaringapp.compose.topbar.nestedcollapse.CollapsingTopBarNestedCollapseElement){}[0] abstract fun (androidx.compose.ui/Modifier).parallax(kotlin/Float): androidx.compose.ui/Modifier // com.flaringapp.compose.topbar/CollapsingTopBarScope.parallax|parallax@androidx.compose.ui.Modifier(kotlin.Float){}[0] + abstract fun (androidx.compose.ui/Modifier).pin(kotlin/Boolean = ...): androidx.compose.ui/Modifier // com.flaringapp.compose.topbar/CollapsingTopBarScope.pin|pin@androidx.compose.ui.Modifier(kotlin.Boolean){}[0] abstract fun (androidx.compose.ui/Modifier).progress(com.flaringapp.compose.topbar/CollapsingTopBarProgressListener): androidx.compose.ui/Modifier // com.flaringapp.compose.topbar/CollapsingTopBarScope.progress|progress@androidx.compose.ui.Modifier(com.flaringapp.compose.topbar.CollapsingTopBarProgressListener){}[0] } diff --git a/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt b/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt index 7655dbb..4988be8 100644 --- a/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt +++ b/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt @@ -255,6 +255,17 @@ public interface CollapsingTopBarScope { */ public fun Modifier.parallax(ratio: Float): Modifier + /** + * Make the element ride with top bar collapse after its bottom touches current top bar bottom. + * + * By default, pinned content continues riding even after crossing the top edge. Set + * [stopAtTop] to true to stop the element at y = 0 instead. + * + * This modifier affects item placement only and does not participate in resolving minimum + * (collapsed) height. + */ + public fun Modifier.pin(stopAtTop: Boolean = false): Modifier + /** * Exclude the element from resolving minimum height among all elements. Useful for 'floating' * elements with custom motion on collapse. @@ -292,6 +303,10 @@ private object CollapsingTopBarScopeInstance : CollapsingTopBarScope { return then(ParallaxModifier(ratio)) } + override fun Modifier.pin(stopAtTop: Boolean): Modifier { + return then(PinModifier(stopAtTop)) + } + override fun Modifier.floating(): Modifier { return then(FloatingModifier()) } @@ -323,6 +338,16 @@ private class ParallaxModifier( } } +private class PinModifier( + private val stopAtTop: Boolean, +) : CollapsingTopBarParentDataModifier() { + override fun modifyParentData(parentData: CollapsingTopBarParentData) { + parentData.pin = CollapsingTopBarParentData.Pin( + stopAtTop = stopAtTop, + ) + } +} + private class FloatingModifier : CollapsingTopBarParentDataModifier() { override fun modifyParentData(parentData: CollapsingTopBarParentData) { parentData.isFloating = true @@ -359,10 +384,16 @@ private abstract class CollapsingTopBarParentDataModifier : ParentDataModifier { private data class CollapsingTopBarParentData( var alignment: Alignment? = null, var parallaxRatio: Float? = null, + var pin: Pin? = null, var isFloating: Boolean = false, var progressListener: CollapsingTopBarProgressListener? = null, var nestedCollapseElement: CollapsingTopBarNestedCollapseElement? = null, -) +) { + + data class Pin( + val stopAtTop: Boolean, + ) +} private val Placeable.topBarParentData: CollapsingTopBarParentData? get() = parentData as? CollapsingTopBarParentData From 93783945d8f9061134f27a9571dbbada80ca7b2f Mon Sep 17 00:00:00 2001 From: Flaringapp Date: Tue, 7 Apr 2026 20:13:10 +0300 Subject: [PATCH 2/5] Update placement logic to account for pin --- .../api/ComposeCollapsingTopBar.klib.api | 2 ++ .../compose/topbar/CollapsingTopBar.kt | 22 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api b/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api index 65719c2..26588b6 100644 --- a/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api +++ b/ComposeCollapsingTopBar/api/ComposeCollapsingTopBar.klib.api @@ -319,6 +319,7 @@ final val com.flaringapp.compose.topbar.scaffold/com_flaringapp_compose_topbar_s final val com.flaringapp.compose.topbar.snap/com_flaringapp_compose_topbar_snap_CollapsingTopBarNoSnapBehavior$stableprop // com.flaringapp.compose.topbar.snap/com_flaringapp_compose_topbar_snap_CollapsingTopBarNoSnapBehavior$stableprop|#static{}com_flaringapp_compose_topbar_snap_CollapsingTopBarNoSnapBehavior$stableprop[0] final val com.flaringapp.compose.topbar.snap/com_flaringapp_compose_topbar_snap_CollapsingTopBarThresholdSnapBehavior$stableprop // com.flaringapp.compose.topbar.snap/com_flaringapp_compose_topbar_snap_CollapsingTopBarThresholdSnapBehavior$stableprop|#static{}com_flaringapp_compose_topbar_snap_CollapsingTopBarThresholdSnapBehavior$stableprop[0] final val com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarLayoutInfo$stableprop // com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarLayoutInfo$stableprop|#static{}com_flaringapp_compose_topbar_CollapsingTopBarLayoutInfo$stableprop[0] +final val com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarParentData_Pin$stableprop // com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarParentData_Pin$stableprop|#static{}com_flaringapp_compose_topbar_CollapsingTopBarParentData_Pin$stableprop[0] final val com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarState$stableprop // com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarState$stableprop|#static{}com_flaringapp_compose_topbar_CollapsingTopBarState$stableprop[0] final fun (androidx.compose.ui/Modifier).com.flaringapp.compose.topbar.dependent/collapsingTopBarDependentStateConnection(com.flaringapp.compose.topbar/CollapsingTopBarState, kotlin/Function1): androidx.compose.ui/Modifier // com.flaringapp.compose.topbar.dependent/collapsingTopBarDependentStateConnection|collapsingTopBarDependentStateConnection@androidx.compose.ui.Modifier(com.flaringapp.compose.topbar.CollapsingTopBarState;kotlin.Function1){}[0] @@ -349,5 +350,6 @@ final fun com.flaringapp.compose.topbar.snap/com_flaringapp_compose_topbar_snap_ final fun com.flaringapp.compose.topbar.snap/rememberCollapsingTopBarSnapBehavior(kotlin/Float, androidx.compose.animation.core/AnimationSpec?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): com.flaringapp.compose.topbar.snap/CollapsingTopBarSnapBehavior // com.flaringapp.compose.topbar.snap/rememberCollapsingTopBarSnapBehavior|rememberCollapsingTopBarSnapBehavior(kotlin.Float;androidx.compose.animation.core.AnimationSpec?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] final fun com.flaringapp.compose.topbar/CollapsingTopBar(androidx.compose.ui/Modifier?, com.flaringapp.compose.topbar/CollapsingTopBarState?, kotlin/Boolean, kotlin/Function3, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.flaringapp.compose.topbar/CollapsingTopBar|CollapsingTopBar(androidx.compose.ui.Modifier?;com.flaringapp.compose.topbar.CollapsingTopBarState?;kotlin.Boolean;kotlin.Function3;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] final fun com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarLayoutInfo$stableprop_getter(): kotlin/Int // com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarLayoutInfo$stableprop_getter|com_flaringapp_compose_topbar_CollapsingTopBarLayoutInfo$stableprop_getter(){}[0] +final fun com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarParentData_Pin$stableprop_getter(): kotlin/Int // com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarParentData_Pin$stableprop_getter|com_flaringapp_compose_topbar_CollapsingTopBarParentData_Pin$stableprop_getter(){}[0] final fun com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarState$stableprop_getter(): kotlin/Int // com.flaringapp.compose.topbar/com_flaringapp_compose_topbar_CollapsingTopBarState$stableprop_getter|com_flaringapp_compose_topbar_CollapsingTopBarState$stableprop_getter(){}[0] final fun com.flaringapp.compose.topbar/rememberCollapsingTopBarState(kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): com.flaringapp.compose.topbar/CollapsingTopBarState // com.flaringapp.compose.topbar/rememberCollapsingTopBarState|rememberCollapsingTopBarState(kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] diff --git a/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt b/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt index 4988be8..b9fc940 100644 --- a/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt +++ b/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt @@ -199,10 +199,24 @@ private fun processPlaceable( layoutDirection = layoutDirection, ) - val parallaxY = parentData?.parallaxRatio?.let { parallaxRatio -> + val parallaxOffsetY = parentData?.parallaxRatio?.let { parallaxRatio -> -(layoutInfo.collapseHeightDelta * parallaxRatio).roundToInt() } ?: 0 + val pinOffsetY = parentData?.pin?.let { pin -> + val baseY = alignmentOffset.y + parallaxOffsetY + val maxAllowedY = layoutInfo.height.roundToInt() - placeable.height + + var pinnedY = baseY.coerceAtMost(maxAllowedY) + if (pin.stopAtTop) { + pinnedY = pinnedY.coerceAtLeast(0) + } + + pinnedY - baseY + } ?: 0 + + val placementOffsetY = parallaxOffsetY + pinOffsetY + val collapsibleSegmentStart = alignmentOffset.y.coerceAtLeast(layoutInfo.collapsedHeight) val collapsibleSegmentEnd = alignmentOffset.y + placeable.height val placeableCollapsibleDistance = @@ -211,9 +225,9 @@ private fun processPlaceable( val placeableProgress = if (placeableCollapsibleDistance == 0) { 1f } else { - val visibleCollapsibleSegmentStart = (collapsibleSegmentStart + parallaxY) + val visibleCollapsibleSegmentStart = (collapsibleSegmentStart + placementOffsetY) .coerceIn(layoutInfo.collapsedHeight, layoutInfo.height.roundToInt()) - val visibleCollapsibleSegmentEnd = (collapsibleSegmentEnd + parallaxY) + val visibleCollapsibleSegmentEnd = (collapsibleSegmentEnd + placementOffsetY) .coerceIn(layoutInfo.collapsedHeight, layoutInfo.height.roundToInt()) val visibleCollapsibleDistance = (visibleCollapsibleSegmentEnd - visibleCollapsibleSegmentStart).coerceAtLeast(0) @@ -226,7 +240,7 @@ private fun processPlaceable( itemProgress = placeableProgress, ) - return IntOffset(alignmentOffset.x, alignmentOffset.y + parallaxY) + return IntOffset(alignmentOffset.x, alignmentOffset.y + placementOffsetY) } /** From 1462c85a54b09e5baad40de24483b3d0ef8f75da Mon Sep 17 00:00:00 2001 From: Flaringapp Date: Tue, 7 Apr 2026 21:34:54 +0300 Subject: [PATCH 3/5] Cleanup code --- .../com/flaringapp/compose/topbar/CollapsingTopBar.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt b/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt index b9fc940..e151f88 100644 --- a/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt +++ b/ComposeCollapsingTopBar/src/commonMain/kotlin/com/flaringapp/compose/topbar/CollapsingTopBar.kt @@ -191,6 +191,8 @@ private fun processPlaceable( topBarSize: IntSize, layoutDirection: LayoutDirection, ): IntOffset { + val currentTopBarHeight = layoutInfo.height.roundToInt() + val parentData = placeable.topBarParentData val alignmentOffset = (parentData?.alignment ?: Alignment.TopStart).align( @@ -205,7 +207,7 @@ private fun processPlaceable( val pinOffsetY = parentData?.pin?.let { pin -> val baseY = alignmentOffset.y + parallaxOffsetY - val maxAllowedY = layoutInfo.height.roundToInt() - placeable.height + val maxAllowedY = currentTopBarHeight - placeable.height var pinnedY = baseY.coerceAtMost(maxAllowedY) if (pin.stopAtTop) { @@ -226,9 +228,9 @@ private fun processPlaceable( 1f } else { val visibleCollapsibleSegmentStart = (collapsibleSegmentStart + placementOffsetY) - .coerceIn(layoutInfo.collapsedHeight, layoutInfo.height.roundToInt()) + .coerceIn(layoutInfo.collapsedHeight, currentTopBarHeight) val visibleCollapsibleSegmentEnd = (collapsibleSegmentEnd + placementOffsetY) - .coerceIn(layoutInfo.collapsedHeight, layoutInfo.height.roundToInt()) + .coerceIn(layoutInfo.collapsedHeight, currentTopBarHeight) val visibleCollapsibleDistance = (visibleCollapsibleSegmentEnd - visibleCollapsibleSegmentStart).coerceAtLeast(0) From 4971a773ea197ac968c1b0054634353b20b09bed Mon Sep 17 00:00:00 2001 From: Flaringapp Date: Tue, 7 Apr 2026 23:15:45 +0300 Subject: [PATCH 4/5] Add pin sample --- .../samples/CollapsingTopBarSampleGroups.kt | 2 + .../shared/ui/samples/advanced/PinSample.kt | 148 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/advanced/PinSample.kt diff --git a/sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/CollapsingTopBarSampleGroups.kt b/sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/CollapsingTopBarSampleGroups.kt index e8ba593..f4955fc 100644 --- a/sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/CollapsingTopBarSampleGroups.kt +++ b/sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/CollapsingTopBarSampleGroups.kt @@ -23,6 +23,7 @@ import com.flaringapp.compose.topbar.sample.shared.ui.samples.advanced.BodyResiz import com.flaringapp.compose.topbar.sample.shared.ui.samples.advanced.FloatingElementSample import com.flaringapp.compose.topbar.sample.shared.ui.samples.advanced.ManualCollapsingControlsSample import com.flaringapp.compose.topbar.sample.shared.ui.samples.advanced.ParallaxCollapsingSample +import com.flaringapp.compose.topbar.sample.shared.ui.samples.advanced.PinSample import com.flaringapp.compose.topbar.sample.shared.ui.samples.advanced.SnapCollapsingSample import com.flaringapp.compose.topbar.sample.shared.ui.samples.basic.CollapsingExitExpandAlwaysSample import com.flaringapp.compose.topbar.sample.shared.ui.samples.basic.CollapsingExitExpandAtTopSample @@ -66,6 +67,7 @@ object CollapsingTopBarSampleGroups { get() = listOf( AlignmentSample, ParallaxCollapsingSample, + PinSample, SnapCollapsingSample, AppBarShadowSample, AppBarScrimSample, diff --git a/sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/advanced/PinSample.kt b/sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/advanced/PinSample.kt new file mode 100644 index 0000000..aedba26 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/flaringapp/compose/topbar/sample/shared/ui/samples/advanced/PinSample.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2026 Flaringapp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.flaringapp.compose.topbar.sample.shared.ui.samples.advanced + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.flaringapp.compose.topbar.sample.shared.ui.samples.CollapsingTopBarSample +import com.flaringapp.compose.topbar.sample.shared.ui.samples.CollapsingTopBarSampleDogDefaults +import com.flaringapp.compose.topbar.sample.shared.ui.samples.common.SampleContent +import com.flaringapp.compose.topbar.sample.shared.ui.samples.common.SampleTopAppBar +import com.flaringapp.compose.topbar.sample.shared.ui.samples.common.SampleTopBarImage +import com.flaringapp.compose.topbar.sample.shared.ui.theme.ComposeCollapsingTopBarTheme +import com.flaringapp.compose.topbar.scaffold.CollapsingTopBarScaffold +import com.flaringapp.compose.topbar.scaffold.CollapsingTopBarScaffoldScrollMode + +object PinSample : CollapsingTopBarSample { + + override val name: String = "Pin" + + @Composable + override fun Content(onBack: () -> Unit) { + PinSampleContent(onBack = onBack) + } +} + +@Composable +fun PinSampleContent( + onBack: () -> Unit, + modifier: Modifier = Modifier, +) { + Surface( + modifier = modifier.fillMaxSize(), + ) { + CollapsingContent( + onBack = onBack, + ) + } +} + +@Composable +private fun CollapsingContent( + onBack: () -> Unit, + modifier: Modifier = Modifier, +) { + CollapsingTopBarScaffold( + modifier = modifier, + scrollMode = CollapsingTopBarScaffoldScrollMode.collapse(expandAlways = false), + topBar = { + SampleTopBarImage( + dog = CollapsingTopBarSampleDogDefaults.Advanced, + ) + + TallElement( + modifier = Modifier + .align(Alignment.Center) + .pin(stopAtTop = true), + ) + + SmallElement( + modifier = Modifier + .floating() + .align(Alignment.BottomEnd) + .pin(), + ) + + SampleTopAppBar( + title = "Pin", + onBack = onBack, + containerColor = Color.Transparent, + ) + }, + body = { + SampleContent() + }, + ) +} + +@Composable +private fun TallElement( + modifier: Modifier = Modifier, +) { + val colorScheme = MaterialTheme.colorScheme + val gradient = remember(colorScheme) { + Brush.verticalGradient( + colors = listOf( + colorScheme.primaryContainer, + colorScheme.primary, + colorScheme.tertiaryContainer, + ), + ) + } + + Box( + modifier = modifier + .size(width = 40.dp, height = 200.dp) + .background(brush = gradient, shape = MaterialTheme.shapes.medium), + ) +} + +@Composable +private fun SmallElement( + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .size(40.dp) + .background( + color = MaterialTheme.colorScheme.primary, + shape = MaterialTheme.shapes.medium, + ), + ) +} + +@Preview +@Composable +private fun Preview() { + ComposeCollapsingTopBarTheme { + PinSampleContent( + onBack = {}, + ) + } +} From 3ffad8c15c4d9344a292a6e37b51d541002977c3 Mon Sep 17 00:00:00 2001 From: Flaringapp Date: Tue, 7 Apr 2026 23:29:20 +0300 Subject: [PATCH 5/5] Update readme --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index abb7ad4..4bba869 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,51 @@ CollapsingTopBarScaffold( +
+Pin + +#### Pin + +```kotlin +Modifier.pin(stopAtTop = false) +``` + +Keeps an element at its regular position until its bottom edge reaches the visible bottom of +`CollapsingTopBar`, then makes it ride upward with the collapsing top bar. + +By default, a pinned element continues riding past the top edge. Set `stopAtTop = true` to stop +it at `y = 0`. + +`pin(...)` affects placement only and does not change minimum height resolution. Combine it with +`floating()` when the pinned element should behave like an overlay and stay out of collapsed height +calculation. + +```kotlin +CollapsingTopBarScaffold( + scrollMode = CollapsingTopBarScaffoldScrollMode.collapse(expandAlways = false), + topBar = { + SampleTopBarImage() + PinnedElement( + modifier = Modifier + .floating() + .align(Alignment.BottomEnd) + .pin(), + ) + SampleTopAppBar( + containerColor = Color.Transparent, + ) + }, + body = { + SampleContent() + }, +) +``` + +> In this example the bottom-end element is pinned and keeps riding with the top bar, excluded from +> collapsed height calculation. + +
+
Floating