My current android applications main screen contains a androidx.compose.material.BackdropScaffold
. The backdrop looks great and functions exactly as it says on the tin.
however I have an issue with the frontLayerContent
which contains a list of items.
I allow the user to interact with the frontLayerContent
list while the backdrop is revealed, the issue is with the backdrop in the revealed state the user cannot scroll down to see the last item in the frontLayerContent
list.
The solution to this issue is to use backdropState.offset
in the modifier
of the frontLayerContent
which I obtain as follows:-
val backdropState = rememberBackdropScaffoldState(initialValue = BackdropValue.Concealed)
var offset by (backdropState.offset as MutableState)
and set as follows:-
onBackdropReveal = {
if (!backdropState.isAnimationRunning) {
scope.launch {
if (backdropState.isConcealed) {
offset = backdropState.offset.value
backdropState.reveal()
} else {
offset = backdropState.offset.value
backdropState.conceal()
}
}
}
}
and make the offset value available to other composables with:-
frontLayerContent = {
CompositionLocalProvider(LocalBackdropStateOffset provides offset) {
frontLayerContent()
}
}
Then in my frontLayerContent
I retrieve the offset value as use as follows:-
val context = LocalContext.current
val offset = LocalBackdropStateOffset.current
Scaffold(
topBar = { Spacer(modifier = Modifier.height(0.dp)) },
modifier = Modifier.fillMaxSize(),
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.offset { IntOffset(x = 0, y = -offset.toInt()) },
verticalArrangement = Arrangement.Top
) {
LazyVerticalGrid(
modifier = Modifier.padding(10.dp),
columns = GridCells.Adaptive(125.dp),
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
content = {
items(state.size) { index ->
this solution is close to what is required, however it looks as though I have made a mistake somewhere as my frontLayerContent
list is always vertically offset even when the backdrop is concealed. In fact revealing or concealing the backdrop does not change the amount of offset of my frontLayerContent
list.
how do I fix this issue?
how can I correctly set the frontLayerContent
list vertical offset depending on whether the backdrop is concealed or revealed?
UPDATE
I only need to "fix" when the backdrop is revealed. therefore i need a conditional configuration of my frontLayerContent
Modifier.offset(y = -offset)
how are you supposed to use the backdropState.offset
value when correcting the frontLayerContent
offset?
as the compose coordinate systems origin is the top left hand corner (x = 0, y = 0)
and y
dimension increases down the screen and x
dimension increases Left to Right. when the backdrop is revealed the offset is a value (on my pixel 5) of approx 600.dp
and concealed value of approx 300.dp
. why is the concealed offset not 0.dp
? is this taking into account the screens TopAppBar
?
UPDATE (2) Heres a GIF I made earlier
UPDATE (3)
This basic sample shows the problem i am having. where have i made my mistake that stops me being able to scroll down to see the complete last item when the backdrop is revealed?
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.BackdropScaffold
import androidx.compose.material.BackdropValue
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.rememberBackdropScaffoldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.elsevier.daisy.ui.theme.MyTheme
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
@ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val scaffoldState = rememberBackdropScaffoldState(initialValue = BackdropValue.Concealed)
val scope = rememberCoroutineScope()
MyTheme {
BackdropScaffold(
scaffoldState = scaffoldState,
frontLayerScrimColor = Color.Unspecified,
frontLayerElevation = 5.dp,
gesturesEnabled = false,
appBar = {
TopAppBar(
title = { Text("Backdrop") },
navigationIcon = {
if (scaffoldState.isConcealed) {
IconButton(
onClick = {
scope.launch { scaffoldState.reveal() }
}
) {
Icon(
Icons.Default.Menu,
contentDescription = "Menu"
)
}
} else {
IconButton(
onClick = {
scope.launch { scaffoldState.conceal() }
}
) {
Icon(
Icons.Default.Close,
contentDescription = "Close"
)
}
}
}
)
},
backLayerContent = {
Column {
Text(
text = "Menu Item 1", modifier = Modifier.padding(8.dp), color = Color.White, style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
color = Color.Black
)
)
Text(
text = "Menu Item 1", modifier = Modifier.padding(8.dp), color = Color.White, style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
color = Color.Black
)
)
}
},
frontLayerContent = {
LazyColumn(modifier = Modifier.fillMaxSize()) {
// Add 5 items
items(55) { index ->
Text(
text = "Item: ${index + 1}",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineLarge,
modifier = Modifier.padding(5.dp)
)
}
}
},
peekHeight = 60.dp,
) {
}
}
}
}
}
I would suggest adding a padding to the LazyColumn/LazyVerticalGrid itself rather than playing with the offset.
Given your example, I would simply add a conditional padding to the LazyColumn:
I've used
64.dp
here but use whatever you feel is necessary according to your design.If instead of using that magic number you want to calculate the height of the
backLayerContent
you can by using theonGloballyPositioned
modifier and save the height in a state.Add this before your
BackdropScaffold
Get the
backLayerContent
height:Then you can use this value for the padding:
Alternative to BackdropScaffold
You may want to create a custom implementation to get a similar behavior without workarounds.
This custom component should help:
Here as an example of how to use it: