27

I have a Column with some rows, and I want to align the last row at the botton, but this row is never located at the bottom of the screen, it stays right after the previous row:

    Column {
        // RED BOX
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .height(130.dp)                
                .padding(vertical = 15.dp, horizontal = 30.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Column {
                Text(
                    text = stringResource(id = R.string.app_name),
                    style = TextStyle(fontSize = 40.sp),
                    color = Color.White
                )
                Text(
                    text = stringResource(id = R.string.app_description),
                    style = TextStyle(fontSize = 13.sp),
                    fontWeight = FontWeight.Bold,
                    color = Color.Black
                )
            }
        }

        Spacer(
            modifier = Modifier
                .fillMaxWidth()
                .height(15.dp)
        )

        // GREEN BOX
        val currentRoute = currentRoute(navController)
        items.forEach { item ->
            DrawerItem(item = item, selected = currentRoute == item.route) {
                navController.navigate(item.route) {
                    launchSingleTop = true
                }

                scope.launch {
                    scaffoldState.drawerState.close()
                }
            }
        }

        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(vertical = 15.dp, horizontal = 30.dp),
            verticalAlignment = Alignment.Bottom,
            horizontalArrangement = Arrangement.Center
        ) {
            Text(
                text = BuildConfig.VERSION_NAME,
                style = TextStyle(fontSize = 11.sp),
                color = Color.Black,
            )
        }
    }

I want to get the same as I show in the picture. I want to have the first row (red), then the second row (green) and then a third row that fits at the bottom of the screen (blue)

enter image description here

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Paul9999
  • 751
  • 1
  • 11
  • 26

3 Answers3

31

You can do it in many ways.

You can use a Column with verticalArrangement = Arrangement.SpaceBetween assigning a weight(1f,false) to the last row:

  Column(
     Modifier.fillMaxHeight(),
     verticalArrangement = Arrangement.SpaceBetween) {
     
     //All elements 
     Column {

        // RED BOX
        
        //...
        Spacer(
            modifier = Modifier
                .fillMaxWidth()
                .background(Green)
                .height(15.dp)
        )

       //... Green box
    }

    //LAST ROW
    Row(
        modifier = Modifier
            .weight(1f, false)
    ) {
        //...
    }
 }

enter image description here

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • 1
    It is not working if my inside column has `verticalScroll()` Is there any way to achieve similar scenario in scrollable content. – Arpit Patel Feb 28 '23 at 18:56
28

You can use a Spacer(modifier.weight(1f)) between GreenBox and Blue Box to create space between them or you can create your custom column with Layout function and set y position of last Placeable as height of Composable - height of last Composble

    Column(modifier = Modifier
        .fillMaxHeight()
        .background(Color.LightGray)) {
        Text(
            "First Text",
            modifier = Modifier
                .background(Color(0xffF44336)),
            color = Color.White
        )
        Text(
            "Second Text",
            modifier = Modifier
                .background(Color(0xff9C27B0)),
            color = Color.White
        )
        Spacer(modifier = Modifier.weight(1f))
        Text(
            "Third Text",
            modifier = Modifier
                .background(Color(0xff2196F3)),
            color = Color.White
        )
    }

Result:

enter image description here

Custom Layout

@Composable
private fun CustomColumn(
    modifier: Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->

        val looseConstraints = constraints.copy(
            minWidth = 0,
            maxWidth = constraints.maxWidth,
            minHeight = 0,
            maxHeight = constraints.maxHeight
        )

        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each child
            measurable.measure(looseConstraints)
        }

        // Track the y co-ord we have placed children up to
        var yPosition = 0


        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Place children in the parent layout
            placeables.forEachIndexed { index, placeable ->

                println("Placeable width: ${placeable.width}, measuredWidth: ${placeable.measuredWidth}")
                // Position item on the screen
                if (index == placeables.size - 1) {
                    placeable.placeRelative(x = 0, y = constraints.maxHeight - placeable.height)
                } else {
                    placeable.placeRelative(x = 0, y = yPosition)
                }

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

Usage

    CustomColumn(
        modifier = Modifier
            .fillMaxHeight()
            .background(Color.LightGray)
    ) {
        Text(
            "First Text",
            modifier = Modifier
                .background(Color(0xffF44336)),
            color = Color.White
        )
        Text(
            "Second Text",
            modifier = Modifier
                .background(Color(0xff9C27B0)),
            color = Color.White
        )
        Spacer(modifier = Modifier.weight(1f))
        Text(
            "Third Text",
            modifier = Modifier
                .background(Color(0xff2196F3)),
            color = Color.White
        )
    }

Result: enter image description here

In this example with Layout you should consider how you measure your measureables with Constraints and your total width and height. It requires a little bit practice but you get more unique designs and with less work(more optimised) composables than ready ones. Here i set layout as maxWidth so no matter which width you assign it takes whole width. It's for demonstration you can set max width or height in layout based on your needs.

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • 2
    setting a specific height of a view seems like a really bad idea. – Marty Miller Nov 21 '22 at 20:00
  • It's for demonstration. Having a fixed height is not the main point in the examples above. Examples above are about placing last Composable at the bottom of parent Composable using a`Spacer` with `Modifier.weight` or creating a custom Composable with `Layout` and placing `placeables` – Thracian Nov 21 '22 at 20:45
3

This is what worked for me:

The structure is something like this:

Column // arrangement = Arrangement.SpaceBetween
  Column
    Row 1
    Row 2
    Row 3
  Row 4
@Preview(showSystemUi = true)
@Composable
fun AlignBottomExample() {
    Column(
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.SpaceBetween
    ) {

        Column() {
            Row(
                modifier = Modifier.height(100.dp)
                    .background(Color.Magenta)
                    .fillMaxWidth(),
            ) {
                Text(text = "Row 1")
            }
            Row(
                modifier = Modifier.height(100.dp)
                    .background(Color.Cyan)
                    .fillMaxWidth(),
            ) {
                Text(text = "Row 2")
            }
            Row(
                modifier = Modifier.height(100.dp)
                    .background(Color.Yellow)
                    .fillMaxWidth(),
            ) {
                Text(text = "Row 3")
            }
        }

        Row(
            modifier = Modifier.height(100.dp)
                .background(Color.Green)
                .fillMaxWidth(),
        ) {
            Text(text = "Row 4")
        }
    }
}

enter image description here

Aakash
  • 21,375
  • 7
  • 100
  • 81