Skip to content

Instantly share code, notes, and snippets.

@garretyoder
Last active March 6, 2024 19:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garretyoder/d1d533f84fc591c6042d586bfce6b498 to your computer and use it in GitHub Desktop.
Save garretyoder/d1d533f84fc591c6042d586bfce6b498 to your computer and use it in GitHub Desktop.
CollapsingAppBarList
/* Copyright 2024 Garret Yoder
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. */
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt
@Composable
fun CollapsingAppBarList(modifier: Modifier = Modifier, collapsedHeight: Dp = 64.dp, collapsedContent: @Composable () -> Unit, expandedHeight: Dp = 128.dp, expandedContent: @Composable () -> Unit, content: LazyListScope.() -> Unit) {
val listState = rememberLazyListState()
val listIsTop by remember {
derivedStateOf {
listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0
}
}
val toolbarHeight: Dp by animateDpAsState(if (listIsTop) expandedHeight else collapsedHeight)
val toolbarHeightPx = with(LocalDensity.current) { collapsedHeight.roundToPx().toFloat() }
var toolbarOffsetHeightPx by remember { mutableStateOf(0f) }
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
toolbarOffsetHeightPx = (toolbarOffsetHeightPx + available.y).coerceIn(-toolbarHeightPx, 0f)
return Offset.Zero
}
}
}
Box(modifier=modifier.nestedScroll(nestedScrollConnection)) {
LazyColumn (Modifier,
contentPadding = PaddingValues(top = toolbarHeight),
state = listState
){
content()
}
Box(Modifier
.fillMaxWidth()
.height(toolbarHeight)
.offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.roundToInt()) },
contentAlignment = Alignment.Center)
{
AnimatedContent(listIsTop) {
when (it) {
true -> {
expandedContent()
}
false -> {
collapsedContent()
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment