I am using motion layout to construct collapsing tool bar .
component looks like this
@Composable
fun CollapsingToolbarWithStartEndIcon(
title: String,
expandedFontSize: Dp = 34.dp,
collapsedFontSize: Dp = 20.dp,
textStyle: TextStyle = MaterialTheme.typography.h6.copy(
color = MaterialTheme.colors.onPrimary
),
backgroundColor: Color = MaterialTheme.colors.primary,
startIcon: Int,
startIconTint: Color = MaterialTheme.colors.onPrimary,
startIconModifier: Modifier = Modifier
.size(defaultIconSize),
endIcon: Int,
endIconTint: Color = MaterialTheme.colors.onPrimary,
endIconModifier: Modifier = Modifier
.size(defaultIconSize),
motionLayoutModifier: Modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.onPrimary),
scrollState: Any,
onStartIconClick: () -> Unit,
onEndIconClick: () -> Unit,
startConstraintSet:ConstraintSet = CollapsingTopBarUtils.startConstraintSet(),
endConstraintSet:ConstraintSet = CollapsingTopBarUtils.endConstraintSet(),
motionTime:Int = 500
) {
when (scrollState) {
is LazyListState -> {
val progress by animateFloatAsState(
targetValue = if (scrollState.firstVisibleItemIndex <= 0) 0f else 1f,
tween(motionTime)
)
val motionHeight by animateDpAsState(
targetValue = if (scrollState.firstVisibleItemIndex <= 0) collapsingTopAppBarHeights else collapsedTopAppBarHeights,
tween(motionTime)
)
val fontSize by animateDpAsState(
targetValue = if (scrollState.firstVisibleItemIndex <= 0) expandedFontSize else collapsedFontSize,
tween(motionTime)
)
MotionLayout(
motionLayoutModifier = motionLayoutModifier,
motionHeight =motionHeight ,
progress = progress,
backgroundColor = backgroundColor,
startIconModifier = startIconModifier ,
onStartIconClick = onStartIconClick,
startIconId = startIcon ,
startIconTint = startIconTint,
endIconModifier = endIconModifier,
onEndIconClick = onEndIconClick,
endIconId = endIcon,
endIconTint = endIconTint,
title = title,
textStyle = textStyle,
fontSize = fontSize,
startConstraintSet = startConstraintSet,
endConstraintSet = endConstraintSet
)
}
is ScrollState -> {
val progress by animateFloatAsState(
targetValue = if (scrollState.value <= 0) 0f else 1f,
tween(motionTime)
)
val motionHeight by animateDpAsState(
targetValue = if (scrollState.value <= 0) collapsingTopAppBarHeights else collapsedTopAppBarHeights,
tween(motionTime)
)
val fontSize by animateDpAsState(
targetValue = if (scrollState.value <= 0) expandedFontSize else collapsedFontSize,
tween(motionTime)
)
MotionLayout(
motionLayoutModifier = motionLayoutModifier,
motionHeight =motionHeight ,
progress = progress,
backgroundColor = backgroundColor,
startIconModifier = startIconModifier ,
onStartIconClick = onStartIconClick,
startIconId = startIcon ,
startIconTint = startIconTint,
endIconModifier = endIconModifier,
onEndIconClick = onEndIconClick,
endIconId = endIcon,
endIconTint = endIconTint,
title = title,
textStyle = textStyle,
fontSize = fontSize,
startConstraintSet = startConstraintSet,
endConstraintSet = endConstraintSet
)
}
}
}
@Composable
@OptIn(ExperimentalMotionApi::class)
private fun MotionLayout(
motionLayoutModifier: Modifier,
motionHeight: Dp,
progress: Float,
backgroundColor: Color,
startIconModifier: Modifier,
onStartIconClick: () -> Unit,
startIconId: Int,
startIconTint: Color,
endIconModifier: Modifier,
onEndIconClick: () -> Unit,
endIconId: Int,
endIconTint: Color,
title: String,
textStyle: TextStyle,
fontSize: Dp,
startConstraintSet:ConstraintSet,
endConstraintSet:ConstraintSet
) {
MotionLayout(
modifier = motionLayoutModifier.height(motionHeight),
start = startConstraintSet,
end = endConstraintSet,
progress = progress,
) {
Box(
modifier = Modifier
.layoutId(BOX)
.testTag(TOP_APP_BAR_BOX)
.background(backgroundColor)
)
IconButton(modifier = startIconModifier
.layoutId(START_ICON)
.testTag(ACTION_ICON1),
onClick = { onStartIconClick.invoke() }) {
Icon(
painter = painterResource(id = startIconId),
tint = startIconTint,
contentDescription = stringResource(R.string.close_icon),
modifier = Modifier.fillMaxSize()
)
}
IconButton(modifier = endIconModifier
.layoutId(END_ICON)
.testTag(ACTION_ICON2),
onClick = { onEndIconClick.invoke() }) {
Icon(
painter = painterResource(id = endIconId),
tint = endIconTint,
contentDescription = stringResource(R.string.email_icon),
modifier = Modifier.fillMaxSize()
)
}
Text(
modifier = Modifier
.layoutId(TITLE)
.testTag(TOP_APP_BAR_TITLE),
text = title,
style = textStyle.copy(fontSize = fontSize.value.sp)
)
}
}
json constraints look like this
object CollapsingTopBarUtils {
@Composable
fun startConstraintSet() = ConstraintSet(
""" {
box: {
width: 'spread',
height: 138,
start: ['parent', 'start'],
end: ['parent', 'end'],
top: ['parent', 'top'],
bottom: ['parent','bottom',16]
},
end_icon:{
end: ['box', 'end', 16],
top: ['box', 'top', 16],
},
start_icon:{
start: ['box', 'start', 16],
top: ['box', 'top', 16]
},
title: {
start: ['box', 'start', 16],
bottom: ['box', 'bottom',16]
}
} """
)
@Composable
fun endConstraintSet() = ConstraintSet(
""" {
end_icon:{
top: ['box','top'],
bottom: ['box', 'bottom'],
end: ['box', 'end',16]
},
box: {
width: 'spread',
height: 56,
start: ['parent', 'start'],
end: ['parent', 'end'],
top: ['parent', 'top'],
},
start_icon:{
start: ['box', 'start',16],
bottom: ['box', 'bottom'],
top: ['box', 'top']
},
title: {
start: ['start_icon', 'end', 16],
bottom: ['start_icon', 'bottom'],
top: ['start_icon', 'top']
}
}"""
)
}
Using this component as like below
setContent {
OnlyLightTheme {
val lazyScrollState = rememberLazyListState()
Scaffold(
modifier = Modifier
.fillMaxSize(),
topBar = {
CollapsingToolbarWithStartEndIcon(
title = "auto",
scrollState =lazyScrollState ,
onStartIconClick = { finish() },
startIconTint = AllstateBlue,
endIconTint = AllstateBlue,
startIcon = R.drawable.icon_close_blue,
endIcon = R.drawable.ic_actions_email_contact,
onEndIconClick = { }
)
},
) { paddingValues ->
Column(modifier = Modifier.padding(paddingValues)) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.background(color = Color.White)
.animateContentSize(),
state = lazyScrollState
) {
val aa = listOf(
"hjhss",
"hhhdhd",
"hfhfh",
"hfhf",
"hhhd",
"hjhss",
"hhhdhd",
"hfhfh",
"hfhf",
"hhhd",
"hjhss",
"hhhdhd",
"hfhfh",
"hfhf",
"hhhd",
"hjhss",
"hhhdhd",
"hfhfh",
"hfhf",
"hhhd",
"hjhss",
"hhhdhd",
"hfhfh",
"hfhf",
"hhhd",
"hjhss",
"hhhdhd",
"hfhfh",
"hfhf",
"hhhd"
)
items(aa) {
Text(text = "Item: $it")
}
}
}
}
}
}
}
}
Collapsing toolbar is not working without padding to the items . if i give paading it works example : below code not works
items(aa) {
Text(text = "Item: $it")
}
where as this works
items(aa) {
Text(text = "Item: $it", modifier = Modifier.padding(16.dp))
}
NOT GETTING WHAT IS MISSING ?
The CollapsingToolbar does not work without the padding because the list does not overflow for the motion layout to activate.
When the padding is inserted, the list overflows past the available screen (viewport) height, thereby enabling the toolbar to collapse where needed
You can add a spacer as an item to the LazyColumn to make it scroll as gotten from this answer