Reputation: 657
I wanted to create and add gestures on top app bar some thing similar to below screenshot using jetpack compose:
I am able to create collapsible top bar using the below android docs link: documentation link but not able to do gestures to expand and collapse along with change in layout using compose. Below is the code I have tried for collapsible toolbar.
val toolbarHeight = 48.dp
val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() }
// our offset to collapse toolbar
val toolbarOffsetHeightPx =
remember { mutableStateOf(0f) }
// now, let's create connection to the nested scroll system and listen to the scroll
// happening inside child LazyColumn
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// try to consume before LazyColumn to collapse toolbar if needed, hence pre-scroll
val delta = available.y
val newOffset = toolbarOffsetHeightPx.value + delta
toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
// here's the catch: let's pretend we consumed 0 in any case, since we want
// LazyColumn to scroll anyway for good UX
// We're basically watching scroll without taking it
return Offset.Zero
}
}
}
And below is the gesture link which I want to implement in topbvar topbar gesture video
Please help me with the links. Thanks!
Upvotes: 7
Views: 5886
Reputation: 7281
If you are looking to implement collapsing toolbar like below where the title will animate based on collapsing state this code reference might help you. You need to build a custom layout for it.
@Composable
fun CollapsingTopBar(
modifier: Modifier = Modifier,
collapseFactor: Float = 1f, // A value from (0-1) where 0 means fully expanded
content: @Composable () -> Unit
) {
val map = mutableMapOf<Placeable, Int>()
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
map.clear()
val placeables = mutableListOf<Placeable>()
measurables.map { measurable ->
when (measurable.layoutId) {
BACK_ID -> measurable.measure(constraints)
SHARE_ID -> measurable.measure(constraints)
TITLE_ID ->
measurable.measure(Constraints.fixedWidth(constraints.maxWidth
- (collapseFactor * (placeables.first().width * 2)).toInt()))
else -> throw IllegalStateException("Id Not found")
}.also { placeable ->
map[placeable] = measurable.layoutId as Int
placeables.add(placeable)
}
}
// Set the size of the layout as big as it can
layout(constraints.maxWidth, constraints.maxHeight) {
placeables.forEach { placeable ->
when (map[placeable]) {
BACK_ID -> placeable.placeRelative(0, 0)
SHARE_ID -> placeable.run {
placeRelative(constraints.maxWidth - width, 0)
}
TITLE_ID -> placeable.run {
val widthOffset = (placeables[0].width * collapseFactor).roundToInt()
val heightOffset = (placeables.first().height - placeable.height) / 2
placeRelative(
widthOffset,
if (collapseFactor == 1f) heightOffset else constraints.maxHeight - height
)
}
}
}
}
}
}
object CollapsingTopBar {
const val BACK_ID = 1001
const val SHARE_ID = 1002
const val TITLE_ID = 1003
const val COLLAPSE_FACTOR = 0.6f
}
@Composable
fun TopBar(
modifier: Modifier = Modifier,
currentHeight: Int,
title: String,
onBack: () -> Unit,
shareShow: () -> Unit
) {
Box(
modifier = modifier.height(currentHeight.dp)
) {
CollapsingTopBar(
collapseFactor = // calculate collapseFactor based on max and min height of the toolbar,
modifier = Modifier
.statusBarsPadding()
) {
Icon(
modifier = Modifier
.wrapContentWidth()
.layoutId(CollapsingTopBar.BACK_ID)
.clickable { onBack() }
.padding(16.dp),
imageVector = Icons.Filled.ArrowBack,
tint = MaterialTheme.colors.onPrimary,
contentDescription = stringResource(id = R.string.text_back)
)
Icon(
modifier = Modifier
.wrapContentSize()
.layoutId(CollapsingTopBar.SHARE_ID)
.clickable { }
.padding(16.dp),
imageVector = Icons.Filled.Share,
tint = MaterialTheme.colors.onPrimary,
contentDescription = stringResource(id = R.string.title_share)
)
Text(
modifier = Modifier
.layoutId(CollapsingTopBar.TITLE_ID)
.wrapContentHeight()
.padding(horizontal = 16.dp),
text = title,
style = MaterialTheme.typography.h4.copy(color = MaterialTheme.colors.onPrimary),
overflow = TextOverflow.Ellipsis
)
}
}
}
Upvotes: 4