Paul9999
Paul9999

Reputation: 1001

How align to bottom a row in Jetpack Compose?

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

Upvotes: 42

Views: 53023

Answers (5)

Thracian
Thracian

Reputation: 66674

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
        )
        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.

Upvotes: 56

Antonio Guerra
Antonio Guerra

Reputation: 145

This approach works for me and also solve if middle content doesn't fit in screen

@Composable
fun FixedTopBottomScrollableMiddleLayout() {
Column(modifier = Modifier.fillMaxSize()) {
    // Top Element
    Text(
        text = "Top Element",
        modifier = Modifier.padding(16.dp)
    )

    // Middle Element (Scrollable)
    Box(modifier = Modifier.weight(1f)) {
        Text(
            text = "This is a long text that will scroll. It demonstrates how to make the middle element scrollable while keeping the top and bottom elements fixed.",
            modifier = Modifier.verticalScroll(rememberScrollState())
        )
    }

    // Bottom Element
    Text(
        text = "Bottom Element",
        modifier = Modifier.padding(16.dp)
    )
}
}

Upvotes: 0

Jakir Hossain
Jakir Hossain

Reputation: 1072

Another solution is using Scaffold:

Scaffold(
    topBar = { /* you can add top bar content  */ },
    bottomBar = {
        Text("Bottom bar",
            fontSize = 50.sp,
            modifier = Modifier
                .fillMaxWidth()
                .background(Color.Black),
        )
    }
) {
    Text(
        "Main content.",
        fontSize = 50.sp,
        color = Color.White,
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight()
            .background(Color.Gray),

    )
} 

Output: enter image description here

Upvotes: 5

Aakash
Aakash

Reputation: 23737

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

Upvotes: 5

Gabriele Mariotti
Gabriele Mariotti

Reputation: 363825

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

Upvotes: 40

Related Questions