androidkotlinuser-interfaceandroid-jetpack-compose

Swipe up to reveal a sheet under the page


I am making an app in Android Studio using Jetpack Compose. I have a "Welcome Page" on my app. Swiping up will reveal several login options in a sheet that will come from the bottom. But I want it to look like the page & sheet are stacked on top of each other, and by swiping up the user will push up the page & reveal the sheet. I added some sketches to show you what I mean. They are not perfect but I hope you understand it. The first picture is the normal state, the second one is the opened one.Rough sketch of my idea

I tried to use ModalBottomSheet, but since it just appears from the bottom, onto the page itself, it wasn't the way I wanted it to be.


Solution

  • There is no standalone BottomSheet Composable that you can use, but you can get a very similar result by using a drawBehind Modifier and drawing a custom border.

    Please have a look at the following code:

    @Composable
    fun WelcomeScreen() {
    
        val cornerRadius = 50.dp
        val cornerRadiusPx = with(LocalDensity.current) { cornerRadius.toPx() }
        val cardBrush = CardDefaults.outlinedCardBorder(true).brush
        val cardStrokeWidthPx = with(LocalDensity.current) { CardDefaults.outlinedCardBorder(true).width.toPx() }
    
        LazyColumn(
            modifier = Modifier.fillMaxSize()
        ) {
            item {
                Column(
                    modifier = Modifier.drawBehind {
                        val y = size.height
                        val x = size.width
    
                        drawLine(
                            brush = cardBrush,
                            start = Offset(cornerRadiusPx, y),
                            end = Offset(x - cornerRadiusPx, y),
                            strokeWidth = cardStrokeWidthPx
                        )
                        drawArc(
                            brush = cardBrush,
                            startAngle = 90f,
                            sweepAngle = 90f,
                            useCenter = false,
                            topLeft = Offset(0f, y - cornerRadiusPx * 2),
                            size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                            style = Stroke(width = cardStrokeWidthPx)
                        )
                        drawArc(
                            brush = cardBrush,
                            startAngle = 0f,
                            sweepAngle = 90f,
                            useCenter = false,
                            topLeft = Offset(x - cornerRadiusPx * 2, y - cornerRadiusPx * 2),
                            size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                            style = Stroke(width = cardStrokeWidthPx)
                        )
                    }
                ) {
                    Column(
                        modifier = Modifier
                            .padding(start = 2.dp, top = 2.dp, end = 2.dp, bottom = cornerRadius)
                            .fillParentMaxSize()
                            .padding(24.dp),
                        verticalArrangement = Arrangement.SpaceBetween,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        Text("\nWELCOME\n", fontSize = 32.sp, fontWeight = FontWeight.Bold)
                        Column(
                            horizontalAlignment = Alignment.CenterHorizontally
                        ) {
                            Text("Log in or Sign up")
                            Icon(
                                imageVector = Icons.Default.KeyboardArrowDown,
                                "Login"
                            )
                        }
                    }
                }
    
            }
            item {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    FilledTonalButton(
                        modifier = Modifier.fillMaxWidth(),
                        onClick = {}
                    ) {
                        Text("LOG IN")
                    }
                    OutlinedButton(
                        modifier = Modifier.fillMaxWidth(),
                        onClick = {}
                    ) {
                        Text("REGISTER")
                    }
                }
            }
        }
    }
    

    We use a LazyColumn so that the content is scrollable easily. Then we define one Column with our custom border using drawBehind. Inside we place another Column that uses the fillParentMaxSize Modifier so that it fills the whole screen at the beginning.

    I am using the brush and stroke width from CardDefaults, feel free to use any other color or stroke width instead.

    Output:

    Screen Recording