How to Use Box Layout in Jetpack Compose

Now that we have an understanding of the Compose Row and Column composables, we will move on to look at the third layout type provided by Compose in the form of the Box component. This chapter will introduce the Box layout and explore some of the key parameters and modifiers that are available when designing user interface layouts.

An introduction to the Box composable

Unlike the Row and Column which organize children in a horizontal row or vertical column, the Box layout stacks its children on top of each other. The stacking order is defined by the order in which the children are called within the Box declaration, with the first child positioned at the bottom of the stack. As with the Row and Column layouts, Box is provided with several parameters and modifiers that can be used to customize the layout.

Creating the BoxLayout project

Begin by launching Android Studio and, if necessary, closing any currently open projects using the File -> Close Project menu option so that the Welcome screen appears.

Select the New Project option from the welcome screen and, when the new project dialog appears, choose the Empty Compose Activity template before clicking on the Next button.

Enter BoxLayout into the Name field and specify com.example.boxlayout as the package name. Before clicking on the Finish button, change the Minimum API level setting to API 26: Android 8.0 (Oreo). On completion of the project creation process, the BoxLayout project should be listed in the Project tool window located along the left-hand edge of the Android Studio main window.

 

You are reading a sample chapter from Jetpack Compose Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

Within the MainActivity.kt file, delete the Greeting function and add a new empty composable named MainScreen:

@Composable
fun MainScreen() {
    
}

Next, change the onCreateActivity() method and DefaultPreview function to call MainScreen instead of Greeting.

Adding the TextCell composable

In this chapter, we will once again use our TextCell composable, though to best demonstrate the features of the Box layout, we will modify the declaration slightly so that it can be passed an optional font size when called. Remaining within the MainActivity.kt file, add this composable function so that it reads as follows:

.
.
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.graphics.Color
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
.
.
@Composable
fun TextCell(text: String, modifier: Modifier = Modifier,  fontSize: Int = 150 ) {
 
    val cellModifier = Modifier
        .padding(4.dp)
        .border(width = 5.dp, color = Color.Black)
    
    Text(
        text = text, cellModifier.then(modifier),
        fontSize = fontSize.sp,
        fontWeight = FontWeight.Bold,
        textAlign = TextAlign.Center
    )
}

Adding a Box layout

Next, modify the MainScreen function to include a Box layout with three TextCell children:

.
.
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
.
.
@Composable
fun MainScreen() {
    Box {
        val height = 200.dp
        val width = 200.dp
 
        TextCell("1", Modifier.size(width = width, height = height))
        TextCell("2", Modifier.size(width = width, height = height))
        TextCell("3", Modifier.size(width = width, height = height))
    }
}

After modifying the function, update the Preview panel to reflect these latest changes. Once the layout appears it should resemble Figure 26-1:

 

You are reading a sample chapter from Jetpack Compose Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

Figure 26-1

The transparent nature of the Text composable allows us to see that the three children have, indeed, been stacked directly on top of each other. While this transparency is useful to show that the children have been stacked, this isn’t the behavior we are looking for in this example. To give the TextCell an opaque background, we need to call the Text composable from within a Surface component. To achieve this, edit the TextCell function so that it now reads as follows:

@Composable
fun TextCell(text: String, modifier: Modifier = Modifier,  fontSize: Int = 150 ) {
.
.
    Surface {
        Text(
            text = text, cellModifier.then(modifier),
            fontSize = fontSize.sp,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center
        )
    }
}

When the preview is updated, only the last composable to be called by the Box will be visible because it is the uppermost child of the stack.

Box alignment

The Box composable includes support for an alignment parameter to customize the positioning of the group of children within the content area of the box. The parameter is named contentAlignment and may be set to any one of the following values:

  • Alignment.TopStart
  • Alignment.TopCenter
  • Alignment.TopEnd
  • Alignment.CenterStart
  • Alignment.Center
  • Alignment.CenterEnd
  • Alignment.BottomCenter
  • Alignment.BottomEnd
  • Alignment.BottomStart

The diagram in Figure 26-2 illustrates the positioning of the Box content for each of the above settings:

 

You are reading a sample chapter from Jetpack Compose Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

Figure 26-2

To try out some of these alignments options, edit the Box declaration in the MainScreen function both to increase its size and to add a contentAlignment parameter:

.
.
import androidx.compose.ui.Alignment
.
.
@Composable
fun MainScreen() {
.
.
    Box(contentAlignment = Alignment.CenterEnd, 
               modifier = Modifier.size(400.dp, 400.dp)) {
        val height = 200.dp
        val width = 200.dp
        TextCell("1", Modifier.size(width = width, height = height))
        TextCell("2", Modifier.size(width = width, height = height))
        TextCell("3", Modifier.size(width = width, height = height))
    }
}

Refresh the preview and verify that the Box content now appears at the CenterEnd position within the Box content area:

Figure 26-3

BoxScope modifiers

In the chapter entitled Jetpack Compose Row and Column Layouts, we introduced ColumnScope and RowScope and explored how these provide additional modifiers that can be applied individually to child components. In the case of the Box layout, the following BoxScope modifiers are available to be applied to child composables:

 

You are reading a sample chapter from Jetpack Compose Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

  • align() – Aligns the child within the Box content area using the specified Alignment value.
  • matchParentSize() – Sizes the child on which the modifier is applied to match the size of the parent Box.

The set of Alignment values accepted by the align modifier is the same as those listed above for Box alignment. The following changes to the MainScreen function demonstrate the align() modifier in action:

@Composable
fun MainScreen() {
.
.
    Box(modifier = Modifier.size(height = 90.dp, width = 290.dp)) {
        Text("TopStart", Modifier.align(Alignment.TopStart))
        Text("TopCenter", Modifier.align(Alignment.TopCenter))
        Text("TopEnd", Modifier.align(Alignment.TopEnd))
 
        Text("CenterStart", Modifier.align(Alignment.CenterStart))
        Text("Center", Modifier.align(Alignment.Center))
        Text(text = "CenterEnd", Modifier.align(Alignment.CenterEnd))
 
        Text("BottomStart", Modifier.align(Alignment.BottomStart))
        Text("BottomCenter", Modifier.align(Alignment.BottomCenter))
        Text("BottomEnd", Modifier.align(Alignment.BottomEnd))
    }
}

When previewed, the above Box layout will appear as shown in Figure 26-4 below:

Figure 26-4

Using the clip() modifier

The compose clip() modifier allows composables to be rendered to conform to specific shapes. Though not specific to Box, the Box component provides perhaps the best example of clipping shapes. To define the shape of a composable, the clip() modifier is called and passed a Shape value which can be RectangleShape, CircleShape, RoundedCornerShape, or CutCornerShape.

The following code, for example, draws a Box clipped to appear as a circle:

 

You are reading a sample chapter from Jetpack Compose Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

.
import androidx.compose.foundation.background
import androidx.compose.ui.draw.clip
import androidx.compose.foundation.shape.CircleShape
.
.
Box(Modifier.size(200.dp).clip(CircleShape).background(Color.Blue))
.
.

When rendered, the Box will appear as shown in Figure 26-5:

Figure 26-5

To draw a composable with rounded corners call RoundedCornerShape, passing through the radius for each corner. If a single radius value is provided it will be applied to all four corners:

.
.
import androidx.compose.foundation.shape.RoundedCornerShape
.
.
Box(Modifier.size(200.dp).clip(RoundedCornerShape(30.dp)).background(Color.Blue))

The above composable will appear as shown below:

Figure 26-6

 

You are reading a sample chapter from Jetpack Compose Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

As an alternative to rounded corners, composables may also be rendered with cut corners. In this case, CutCornerShape is passed the cut length for the corners. Once again different values may be specified for each corner, or all corners cut equally with a single length parameter:

.
.
import androidx.compose.foundation.shape.CutCornerShape
.
.
Box(Modifier.size(200.dp).clip(CutCornerShape(30.dp)).background(Color.Blue))
.
.

The following figure shows the Box rendered by the above code:

Figure 26-7

Summary

The Compose Box layout positions all of its children on top of each other in a stack arrangement with the first child positioned at the bottom of the stack. By default, this stack will be positioned in the top left-hand corner of the content area, though this can be changed using the contentAlignment parameter when calling the Box composable.

Direct children of a Box layout have access to additional modifiers via RowScope. These modifiers allow individual children to be positioned independently within the Box content using a collection of nine pre-defined position settings.

 

You are reading a sample chapter from Jetpack Compose Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print