I am trying to collapsing the App Bar with my Custom NestedScrollConnection but it make space in App Bar place when I Scroll. The App Bar goes up when I scroll but the space of App Bar in the Background of App Bar doesn’t go up?
My Custom NestedScrollConnection Class:
class CollapsingAppBarNestedScrollConnection(
val appBarMaxHeight: Int,
) : NestedScrollConnection {
var appBarOffset by mutableIntStateOf(0)
private set
override fun onPreScroll(
available: Offset,
source: NestedScrollSource,
): Offset {
val delta = available.y.roundToInt()
val newsOffset = appBarOffset + delta
val previousOffset = appBarOffset
appBarOffset = newsOffset.coerceIn(
-appBarMaxHeight,
0
)
val consumed = appBarOffset - previousOffset
return Offset(0f, consumed.toFloat())
}
}
MY UI:
val density = LocalDensity.current
val appBarMaxHeight = with(density) { (70).dp.roundToPx() }
val connection = remember(appBarMaxHeight) {
CollapsingAppBarNestedScrollConnection(appBarMaxHeightPx)
}
Scaffold(
topBar = {
TopAppBar(
title = {
Text("Top App Bar")
},
modifier = Modifier
.offset { IntOffset(0, connection.appBarOffset) }
)
},
modifier = Modifier
.fillMaxSize()
.nestedScroll(connection)
) {
// Content
}
Video gif, before
Video gif, After
2
When you use the offset
Modifier, the space where the Composable originally was remains reserved. This is because
it avoids recomposition when the offset is changing, and also adds a graphics layer that prevents unnecessary redrawing of the context when the offset is changing.
Thus, the offset is applied on another layer and does not actually impact the Composable hierarchy. This means that you can’t use the space where the TopAppBar
originally was for other Composables.
To achieve this, you would usually use a default NestedScrollConnection
like TopAppBarDefaults.enterAlwaysScrollBehavior
.
If you actually need a custom NestedScrollConnection
the TopAppBar
to shift out of the screen at scrolling, implement this using the height
Modifier together with wrapContent
:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyCollapsibleTopAppBarDemo() {
val density = LocalDensity.current
val myNestedScrollConnection = remember {
MyNestedScrollConnection(TopAppBarDefaults.TopAppBarExpandedHeight.dpToPx(density))
}
Scaffold(
modifier = Modifier
.fillMaxSize()
.nestedScroll(myNestedScrollConnection),
topBar = {
CenterAlignedTopAppBar(
modifier = Modifier
.height(myNestedScrollConnection.currentHeaderHeightPx.toInt().pxToDp(density))
.wrapContentHeight(Alignment.Bottom, true),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.LightGray
),
title = {
Text("Demonstration")
}
)
}
) { innerPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
items(50) { item ->
Text("Item $item", modifier = Modifier.padding(16.dp))
}
}
}
}
fun Dp.dpToPx(density: Density) = with(density) { [email protected]() }
fun Int.pxToDp(density: Density) = with(density) { [email protected]() }
Update your custom NestedScrollConnection
like this:
class MyNestedScrollConnection(
val appBarMaxHeight: Float,
) : NestedScrollConnection {
var currentHeaderHeightPx by mutableFloatStateOf(appBarMaxHeight)
private set
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
// add this if statement when you only want the TopAppBar to appear when scrolled fully to the top
if (delta >= 0) {
return Offset.Zero
}
//
val newHeaderHeightPx = currentHeaderHeightPx + delta
val previousHeaderHeightPx = currentHeaderHeightPx
currentHeaderHeightPx = newHeaderHeightPx.coerceIn(0f, appBarMaxHeight)
val consumed = currentHeaderHeightPx - previousHeaderHeightPx
return Offset(x = 0f, y = consumed)
}
// additionally add this fun when you only want the TopAppBar to appear when scrolled fully to the top
// this code is very similar to onPreScroll, so you could extract a common method for it
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
val delta = available.y
val newHeaderHeightPx = currentHeaderHeightPx + delta
val previousHeaderHeightPx = currentHeaderHeightPx
currentHeaderHeightPx = newHeaderHeightPx.coerceIn(0f, appBarMaxHeight)
val consumedLocally = currentHeaderHeightPx - previousHeaderHeightPx
return Offset(x = 0f, y = consumedLocally)
}
}
Output:
Note:
This does not animate the CenterAlignedTopAppBar
color when the content is scrolled.
8
I found the Problem I just Add Height Modifier in My App Bar
.
so whenever I scroll so it also moves the space that remains in the App Bar
I have to gave the exact height to the Composable that I don’t want. how Can I gave the Wrap Height or without Height
and How You add Video, How can I Add Video?
// Top Bar Scroll Connection
val topBarMaxHeight: Int = with(density) { (30).dp.roundToPx() }
val topBarScrollConnection: CollapsingAppBarNestedScrollConnection =
remember(topBarMaxHeight) {
CollapsingAppBarNestedScrollConnection(topBarMaxHeight)
}
CenterAlignedTopAppBar(
title = {
Text(
text = if(tabPagerState.currentPage == 0) "News" else "Events",
)
},
navigationIcon = {
IconButton(onClick = {
scope.launch {
drawerState.open()
}
}) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = "Icon for Navigation Drawer",
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent
),
modifier = Modifier
.statusBarsPadding()
.then(
with(density) {
Modifier
.height( // I Add this Height
(topBarMaxHeight + topBarScrollConnection.appBarOffset).toDp()
)
}
)
.offset { IntOffset(0, topBarScrollConnection.appBarOffset) }
)
I Add Height so when ever I scroll so the it moves the height of TopAppBar Also that before it remains.