DEV Community

Ramsingh Manek
Ramsingh Manek

Posted on

Complete Guide to Layouts in Compose - Android Jetpack Compose

The Layouts composable are a very essential and important component for creating UI. Jetpack compose provides predefined layouts like Column, Row, Box, and ConstraintLayout.

Common Attributes for the layout composable:

Showing widgets vertically using Column:

In Composable there is a Column as a LinearLayout with vertical orientation. It simply layouts all child widgets as vertical columns. All the children in the Column layout will be arranged vertically at the top position and in horizontal alignment at the start position, by default. Update the above function as below:

@Preview
@Composable
fun ColumnExample() {

Column(
modifier = Modifier.fillMaxSize()
) {
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null
)
Text(text = "Espresso", fontSize = 25.sp)
}
}

In the above example, we have not used any alignment-related property. So let's see how the output would be if we set alignment to image and text compostables.

Weight

We can divide the components into equal portions using the weight in the modifier in the Column layout using the weight(weight value) modifier. Let’s see a basic example below:

@Preview
@Composable
fun ColumnExampleWithWeight() {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
) {
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null,
Modifier.weight(2f)
)
Text(
text = "Espresso",
fontSize = 25.sp,
modifier = Modifier
.weight(2f)
.align(Alignment.CenterHorizontally)
)
}
}

Layout Alignment

By default, all children in the Column are aligned in the top-left corner position. We can align the children in the Column layout using the following alignments

With the Column layout, we have constant Alignment.CenterHorizontal interface are:

1. Start 

2.CenterHorizontally

3. End

The above parameter is used to align the components horizontally along the horizontal axis.

Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null
)
Text(text = "Espresso", fontSize = 25.sp, color = Color.White)
}

Layout Arrangement

The arrangement can be used to control the positions of the child along the same axis as the parent container like vertically for Column. Arrangements values can be set on the Column layout using vertical arrangement parameters. 

The column composable has a vertical arrangement parameter which accepts the values as follows

1. Arrangement.Top - Arrange the content at the top of the content area vertically

2. Arrangement. Center - Arrange the content at the center vertically in the content area

3. Arrangement.Bottom - Arrange the content at the bottom of the content area vertically.

Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null
)
Text(text = "Espresso", fontSize = 25.sp, color = Color.White)
}

Showing widgets horizontally using Row:

In Composable there is a Row as a LinearLayout with horizontal orientation. It simply layouts all child widgets as horizontal rows. All the children in the Row layout will be arranged vertically at the top position and in horizontal alignment at the start position, by default. Update the above function as below:

Row(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
) {
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null,
modifier = Modifier.size(100.dp)
)
Text(text = "Espresso", fontSize = 25.sp, color = Color.White)
}

Weight

We can add the weight in the modifier in the Row layout using the weight(weight value) modifier. Let’s see a basic example below:

@Preview
@Composable
fun RowExampleWithWeight() {

Row(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
) {
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null,
modifier = Modifier.weight(1f)
)
Text(
text = "Espresso",
fontSize = 25.sp,
color = Color.White,
modifier = Modifier.weight(1f)
)
}
}

Layout Alignment

By default, all children in Row are aligned in the top-left corner position. We can align the children in the Row layout using the following alignments

With the Row layout, we have constant Alignment.CenterVertical interface is:

4. Top

5.CenterVertically

6. Bottom

The above parameter is used to align the components horizontally along the horizontal axis.

Layout Arrangement

The arrangement can be used to control the positions of the child along the same axis as the parent container like horizontally for Rows. Arrangements values can be set on the Row layout using horizontal arrangement parameters.

The column composable has a vertical arrangement parameter that accepts the values as follows

1. Arrangement. Start - Arrange the content at the start of the row horizontally to the content area

2. Arrangement.Center - Arrange the content at the center horizontally in the content area

3. Arrangement.End - Arrange the content at the end of the row to the content area horizontally.

Row(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
verticalAlignment =Alignment.CenterVertically
) {
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null,
modifier = Modifier.weight(1f)
)
Text(
text = "Espresso",
fontSize = 25.sp,
color = Color.White,
modifier = Modifier.weight(1f)
)
}

Layout Arrangement Spacing

Arranging space between each child component in Rows or Columns is spaced across the content area. The spacing can be achieved by using the horizontal arrangement and vertical arrangement parameters that require one of the following values.

1. Arrangement.SpaceEvenly - All child components are spaced equally, including before the first component and after the last component.

2. Arrangement.SpaceBetween - All children are spaced equally in the parent layout. There is no space added before the first and after the last child.

3. Arrangement.SpaceAround - All children are spaced equally in the parent layout. It includes half spacing before the first and after the last child. 

Below example are used with Row composable

1.SpaceEvenly:

@Composable
fun TextCell(value: String) {
val cellModifier = Modifier
.padding(5.dp)
.size(100.dp, 100.dp)
.border(width = 2.dp, color = Color.Red)
.wrapContentHeight()

Text(
text = value,
modifier = cellModifier,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}

Now, call the above function to create Text composable as follows:

Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxSize()
) {
TextCell(value = "A")
TextCell(value = "B")
TextCell(value = "C")
}

2.SpaceBetween

Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxSize()
) {
TextCell(value = "A")
TextCell(value = "B")
TextCell(value = "C")
}

3.SpaceAround

Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround,
modifier = Modifier.fillMaxSize()
) {
TextCell(value = "A")
TextCell(value = "B")
TextCell(value = "C")
}

Below examples are used with Column composable. Also, we will use the same TextCell() for column example

1.SpaceEvenly

Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.height(600.dp)
) {
TextCell(value = "A")
TextCell(value = "B")
TextCell(value = "C")
}

2.SpaceBetween

Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.height(600.dp)
) {
TextCell(value = "A")
TextCell(value = "B")
TextCell(value = "C")
}

3.SpaceAround

Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround,
modifier = Modifier.height(600.dp)
) {
TextCell(value = "A")
TextCell(value = "B")
TextCell(value = "C")
}

Box Layout

Boxes are the third most used composable when developing applications using jetpack compose. The box composable will stack the child on top of each other. By default, the children will be placed at the top-start position. The stacking order is the order in which children are declared in the box layout, the first child will be at the bottom of the stack and the last child will be visible on the top of the stack.

Aligning the child in the Box Layout

Using the Box layout we can specify the position of the child where we want to align our child elements inside our box. There are 3 vertical options available top, center, and bottom. Horizontal options are, start, center, and end. So there are 9 possible alignments for our child's in-box layout as shown below table:

Here is a simple example of a box layout

Box(contentAlignment = Alignment.Center,
modifier = Modifier.size(400.dp, 400.dp)) {

TextCell("1")
TextCell("2")
TextCell("3")
}

BoxScope modifiers

The following BoxScope modifiers are available that need to be applied to child components in the box layout.

1. align() - Aligns the child to a specified alignment value within the Box content layout. The set of alignment values is the same as those listed above for box content alignment.

2.matchParentSize() - If this modifier is specified to the child then the size of the child will be the same as the parent box.

Now, add the following function to add text cells in the box layout as follows to add text layout to all different positions.

@Composable
fun TextCellBox(value: String, modifier: Modifier) {
val cellModifier = Modifier
.wrapContentHeight()
.size(30.dp, 30.dp)
.border(width = 2.dp, color = Color.Red)

Text(
text = value,
modifier = cellModifier.then(modifier),
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}

Here is the box example for adding text to all positions.

Box(modifier = Modifier.size(height = 150.dp, width = 200.dp).padding(15.dp)) {
TextCellBox(value = "A", modifier = Modifier.align(Alignment.TopStart))
TextCellBox(value = "B", modifier = Modifier.align(Alignment.TopCenter))
TextCellBox(value = "C", modifier = Modifier.align(Alignment.TopEnd))
TextCellBox(value = "D", modifier = Modifier.align(Alignment.CenterStart))
TextCellBox(value = "E", modifier = Modifier.align(Alignment.Center))
TextCellBox(value = "F", modifier = Modifier.align(Alignment.CenterEnd))
TextCellBox(value = "G", modifier = Modifier.align(Alignment.BottomStart))
TextCellBox(value = "H", modifier = Modifier.align(Alignment.BottomCenter))
TextCellBox(value = "I", modifier = Modifier.align(Alignment.BottomEnd))

}

Let's now check how the matchParentSize() modifier will work by a simple example.

Box(
Modifier
.size(200.dp)
.background(Color.Blue)
)
{

Text(
text = "CENTER",
modifier = Modifier
.padding(10.dp)
.background(Color.Red)
.matchParentSize()
.wrapContentHeight(),
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)

}

Using the clip() modifier

The clip() modifier is used to render the specified shape to box composable layout. To define the shape of the composable, the clip() modifier is called with values such as RectangleShape, CircleShape, RoundedCornerShape, or CutCornerShape. 

The following example will clip the box composable and draw the circle with blue color. When rendered it will look like the box appears in the below image.

Box(
Modifier
.size(300.dp)
.padding(50.dp)
.clip(CircleShape)
.background(Color.Blue)
)

To draw different shapes like rounded corners just change the value in the clip() modifier as RoundedCorderShape, and pass the radius for each direction to it to draw the required corners. If only a single value is passed as a radius then it will be applied the same to all corners. Let's see a simple example with a rounded corner box and a simple “Hello” text inside it.

Box(
Modifier
.clip(RoundedCornerShape(15.dp))
.size(300.dp)
.padding(50.dp)
.background(Color.Blue)
)
{

Text(
text = "Hello!",
modifier = Modifier
.padding(20.dp)
.clip(RoundedCornerShape(15.dp))
.background(Color.Red)
.matchParentSize()
.wrapContentHeight(),
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}

Constraint Layout

The ConstraintLayout positions the children according to the constraint settled between them. It is similar to “ConstraintLayout” which we use to add views in XML for Android Development.

Generating References

By default, the children in ConstraintLayout will be positioned on the top-left-hand corner of the content area with the assumption of the app running on left to right and top-to-bottom locale. Before assigning the constraints the compostables must assign a reference. There are two steps in the process generating the references and assigning them to the composable before applying the constraints. 

createRef() function is used to create a single reference and result assigned to constant, for example

        Val text1 = createRef()
Enter fullscreen mode Exit fullscreen mode

Alternatively, if you want to create multiple references in a single step by calling same createRefs() function as shown below:

Val (text1, text2, text3) = createRefs()

Assigning references to a composable 

After creating the reference, it can be applied to an individual composable using the constraints () modifier function. For example, assign text1 reference to Text composable. As we can see in the given example, the constraints () modifier function has a trailing lambda in which we can specify the constraint for the composable.

ConstraintLayout{
Val img = createRef()
Image(
Bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small), contentDescription = null, modifier = Modifier.constraintAs(img){
// add constraints here
})

}

Adding Constraint

The most common constraint of the composable is between one side of the composable and another side of either another composable or parent ConstraintLayout itself. The constraints are added using the constraints () modifier function and declared inside the training lambda block via the call to link () function.

Let’s see how we can implement constraint layout by using the link () function as shown below by updating the above sample code.

Compose example 1.0

ConstraintLayout{
Val img = createRef()
Image(
Bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small), contentDescription = null, modifier = Modifier.constraintAs(img){
top.linkTo(parent.top, margin = 16.dp)
start.linkTo(parent.start, margin = 16.dp)
})

}

In the above example, we have set the constraint of the image view to the left - the top position of the parent ConstraintLayout instance, both with 16. dp margin.

In addition to the linkTo() function there are other functions to apply constraints, a component can be centered horizontally or vertically relative to the parent itself or any other component using centerHorizontallyTo() and centerVerticallyTo() respectively. For example,

Image(
Bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small), contentDescription = null, modifier = Modifier.constraintAs(img){
centerVerticallyTo(parent)
centerHorizontallyTo(parent)
})

In the above example, the image will be positioned vertically from center to parent on the vertical axis, and horizontally from center to parent on the horizontal axis of the ConstraintLayout.

centerAround() function can be used to position the component in the center vertically or horizontally relative to the side of another component.

Now, after learning to add constraints let’s create a simple view with an image and some text as below.

ConstraintLayout(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
val img = createRef()
val txtTitle = createRef()
val txtDescription = createRef()
val txtTagline = createRef()
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null,
modifier = Modifier
.constrainAs(img) {
top.linkTo(parent.top, margin = 16.dp)
start.linkTo(parent.start, margin = 16.dp)
}
.background(Color.Blue)
.clip(RoundedCornerShape(10.dp))
.size(80.dp)
.wrapContentHeight()
)

   Text("Espresso", modifier = Modifier.constrainAs(txtTitle) {
       top.linkTo(parent.top)
       linkTo(start = img.end, end = parent.end, startMargin = 10.dp, endMargin = 16.dp)
       width = Dimension.fillToConstraints
   }, fontSize = 24.sp, color = Color.White)

   Text(
       text = "Espresso is coffee of Italian origin, brewed by forcing a small amount of nearly boiling water under pressure (expressing) through finely-ground coffee beans.",
       modifier = Modifier.constrainAs(txtDescription) {
           top.linkTo(txtTitle.bottom, margin = 5.dp)
           linkTo(txtTitle.start, txtTitle.end)
           width = Dimension.fillToConstraints
       }, color = Color.White
   )

   Text(
       text = "This is example of ConstraintLayout",
       modifier = Modifier.constrainAs(txtTagline) {
           linkTo(parent.start, parent.end)
           top.linkTo(txtDescription.bottom, margin = 10.dp)
       }, color = Color.White
   )
Enter fullscreen mode Exit fullscreen mode

}

Opposing Constraints

Till now we have seen how to set constraints to components to a fixed position within the parent with margins. The opposing constraints are the constraints created with both sides of the component along the same axis horizontally or vertically are constrained. For example, applying the opposing constraint on the button on the horizontal axis.

val btnOpposed = createRef()
Button(onClick = { }, modifier = Modifier.constrainAs(btnOpposed) {
top.linkTo(txtTagline.bottom, margin = 20.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}) {
Text("Centered Button")
}

As you can see in the above image the opposing constraint will position the component in the horizontal center position in the constraint layout. The link () function can accept the parameters to concise the opposing constraint which can be declared as the following example, by updating the above-given code.

val btnOpposed = createRef()
Button(onClick = { }, modifier = Modifier.constrainAs(btnOpposed) {
top.linkTo(txtTagline.bottom, margin = 20.dp)
linkTo(parent.start, parent.end)
}) {
Text("View Detail")
}

The link to the () function has two arguments in the lambda block, which will position the button in the center horizontally with a single line of code. If you require to position the component center vertically or center horizontally, within the parent, the same result can be achieved using the following line of code.

centerVerticallyTo(parent)
centerHorizontallyTo(parent)

Constraint Bais

To arrange a component to center position on a particular axis opposing constraint is used. When the bias is applied to a constrained component, it will be moved relative to the available space. Let's understand the basis constraint with a simple example. 

Add the following code to the previous compose example 1.0 and update the button code block as below.

val btnOpposed = createRef()
val (imgLlike, imgShare) = createRefs()

Image(
Icons.Filled.Favorite,
contentDescription = null,
modifier = Modifier.constrainAs(imgLlike) {
linkTo(btnOpposed.top, btnOpposed.bottom)
linkTo(parent.start, parent.end, bias = 0.05f)
},
colorFilter = ColorFilter.tint(Color.White)
)

Image(
Icons.Filled.Share,
contentDescription = null,
modifier = Modifier.constrainAs(imgShare) {
linkTo(btnOpposed.top, btnOpposed.bottom)
linkTo(parent.start, parent.end, bias = 0.15f)
}, colorFilter = ColorFilter.tint(Color.White)
)

Button(onClick = { }, modifier = Modifier.constrainAs(btnOpposed) {
top.linkTo(txtTagline.bottom, margin = 20.dp)
linkTo(parent.start, parent.end, bias = 0.9f)
}) {
Text("View Detail")
}

In this example button “View Detail” will be positioned at 90% of the width of the parent. Same way icons of share and favorite have been constrained using bias and positioned at 5% and 15% of the parent.

Constraint Margin

We can set the margin to a composable to fix the gap according to our needs, between a component and another element like a composable barrier, guideline, or the side of the parent ConstraintLayout.

Please consider the above example for margin. The horizontal bias constraint setting will control the position of the component on the right-hand side of the layout.

After adding a margin constraint to a component a fixed gap appears and a component can not be moved even when adjusting the bias.  In the code given below margin of 30 dp is added and the right-hand constraint into the button is composable and can not be moved even when setting bias to 100%.

val btnClose = createRef()
Button(onClick = {}, modifier = Modifier.constrainAs(btnClose) {
bottom.linkTo(parent.bottom, margin = 30.dp)
linkTo(parent.start, parent.end, bias = 1.0f, endMargin = 30.dp)
}) {
Text(text = "Close")
}

This margin on the right-hand side of the button will be preserved even if screen orientation changes or any other component or view are added to the right side of the button. Even if we don’t specify the bias setting, the margin will be applied and will not affect the positioning of the component.

Working with Chains in ConstraintLayout

When we need to arrange more than two components on the same axis horizontally or vertically with equal spacing around them we can use Chain constraint in ConstraintLayout. A chain constraint for two or more components can be created by using one of the functions either createHorizontalChain() or createVerticalChain().

Both these functions require passing parameters of the component references created using createRef() or createRefs(). Let's understand chain constraint by a simple example, arranging the 3 buttons horizontally with equal spacing around them.

val (obtnFood, obtnFashion, obtnTravel) = createRefs()
createHorizontalChain(obtnFood, obtnFashion, obtnTravel)

OutlinedButton(
onClick = { },
modifier = Modifier
.constrainAs(obtnFood) {
bottom.linkTo(btnClose.top, margin = 20.dp)
centerHorizontallyTo(parent)
}) {
Text(text = "Food")
}
OutlinedButton(
onClick = { },
modifier = Modifier
.constrainAs(obtnFashion) {
bottom.linkTo(btnClose.top, margin = 20.dp)
centerHorizontallyTo(parent)
}) {
Text(text = "Fashion")
}
OutlinedButton(
onClick = { },
modifier = Modifier
.constrainAs(obtnTravel) {
bottom.linkTo(btnClose.top, margin = 20.dp)
centerHorizontallyTo(parent)
}) {
Text(text = "Travel")
}

A chain constraint can also be arranged using Packed, Spread, or SpreadInside styles. 

1. Packed: A style where all the components are grouped together and placed in the center of the available space.

2. Spread: A style where all components are evenly distributed

3.SpreadInside: A style where all components are evenly distributed but the first and last components are fixed to each end.

To change the style, you need to add a style parameter to createHorizontalChain() or createVerticallyChain(). Changing the style from the default Spread chain to SpreadInside is as follows.

createHorizontalChain(
obtnFood,
obtnFashion,
obtnTravel,
chainStyle = ChainStyle.SpreadInside
)

After changing the chain style to SpreadInside the following preview will be generated in the preview pane.

Working with Guidelines 

ConstraintLayout provides horizontal or vertical guidelines. Guidelines are invisible lines that are not visible to users. Guidelines are helpful to the developer to constrain views and design layouts easily. Guidelines are useful when a number of components need to align to a specific axis line like horizontally or vertically. A guideline can have the value as a percentage of either the height or width of the parent. 

The following function call will create a vertical guideline to the starting edge of the parent. 

createGuidelineFromStart(fraction = 0.25f)

The following function call will create a horizontal guideline to the bottom edge of the parent and the guideline will be positioned above the 60dp from the bottom edge.

createGuidelineFromBottom(offset = 60.dp)

Now, let's create a simple example using guidelines at 40% of the parent and show the banner image at the top above the guideline, and below the image we can show some description or other content.

Below the banner image, we have set the like and share images. And at the bottom, there is a button “View Detail”. The like and share images are again set with a vertical guideline, on the first half we have set the Like image, and on the other half we have set the Share image.

ConstraintLayout(
modifier = Modifier
.fillMaxSize()
) {

val (img, imgLlike, imgShare, btnViewDetail) = createRefs()
val guideLineCoverImage = createGuidelineFromTop(0.4f)
val guideLineMiddleVerticalLine = createGuidelineFromStart(0.5f)

Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.espresso_small),
contentDescription = null,
modifier = Modifier
.constrainAs(img) {
top.linkTo(parent.top)
bottom.linkTo(guideLineCoverImage)
height = Dimension.fillToConstraints
}
.background(Color.Blue)
.wrapContentWidth()
.fillMaxHeight()
)

Image(
Icons.Filled.Favorite,
contentDescription = null,
modifier = Modifier
.constrainAs(imgLlike) {
top.linkTo(img.bottom, 10.dp)
linkTo(parent.start, guideLineMiddleVerticalLine)
}
.size(50.dp),
colorFilter = ColorFilter.tint(Color.Gray)
)

Image(
Icons.Filled.Share,
contentDescription = null,
modifier = Modifier
.constrainAs(imgShare) {
top.linkTo(img.bottom, 10.dp)
linkTo(guideLineMiddleVerticalLine, parent.end)
}
.size(50.dp),
colorFilter = ColorFilter.tint(Color.Gray)
)

Button(onClick = { }, modifier = Modifier.constrainAs(btnViewDetail) {
linkTo(parent.start, parent.end)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints
}) {
Text("View Detail")
}
}

Here is the preview of the above code. In the image there you can see the vertical dotted line between the Like and Share images which is a guideline but when we run it is not visible to the user. 

Also, let's take another simple example for showing buttons using guidelines.

ConstraintLayout(
modifier = Modifier
.wrapContentSize()
.padding(30.dp)
) {
val (button1, button2, button3) = createRefs()
val verticalGuideline = createGuidelineFromStart(0.6f)

   Button(onClick = { }, modifier = Modifier.constrainAs(button1) {
       top.linkTo(parent.top)
       end.linkTo(verticalGuideline, margin = 20.dp)
   }) {
       Text("Button 1")
   }

   Button(onClick = { }, modifier = Modifier.constrainAs(button2) {
       top.linkTo(button1.bottom)
       start.linkTo(verticalGuideline, margin = 40.dp)
   }) {
       Text("Button 2")
   }

   Button(onClick = { }, modifier = Modifier.constrainAs(button3) {
       top.linkTo(button2.bottom)
       end.linkTo(verticalGuideline, margin = 20.dp)
   }) {
       Text("Button 3")
   }
Enter fullscreen mode Exit fullscreen mode

}

Working with barriers in ConstraintLayout

The barrier constraints are created for a specific side of one or more components using the following 4 available functions.

1.createStartBarrier()

2.createEndBarrier()

3.createTopBarrier()

4.createBottomBarrier()

Each of the functions accepts the reference of the component that needs to create a barrier with optional margin parameters.

val barrier = createEndBarrier(button1, button2, margin = 30.dp)

createEndBarrier() function will create the vertical barrier positioned 30 dp from the widest width of button1 or button2. So start and the end is used to create vertical barriers and the top and bottom are used for horizontal barriers. 

To learn about barriers, let's create a sample code to understand scenarios. Implement the following sample code.

ConstraintLayout(
modifier = Modifier
.size(300.dp, 200.dp)
.padding(10.dp)
) {

val (button1, button2, button3) = createRefs()

Button(onClick = { }, modifier = Modifier
.constrainAs(button1) {
top.linkTo(parent.top)
start.linkTo(parent.start)
}
.width(100.dp)) {
Text("Button 1")
}

Button(onClick = { }, modifier = Modifier
.constrainAs(button2) {
top.linkTo(button1.bottom, margin = 20.dp)
start.linkTo(parent.start)
}
.width(100.dp)) {
Text("Button 2")
}

Button(onClick = { }, modifier = Modifier.constrainAs(button3) {
linkTo(parent.top, parent.bottom)
linkTo(button1.end, parent.end, startMargin = 10.dp)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints

}) {
Text("Button 3")
}
}

In the above example, button 3 is arranged to the right-hand side of button 1. And button3 needed its size to fill with available maximum free space. Also need to ensure that if the size of button1 and button2 is changed button3 should be changed in response to them.

In the above code sample if button1’s size is set to 200. dp which is increased its width then button3’s size will get changed see the below screenshot.

Now, if button2’s width changes that will not affect button3’s size but it affect that button2 overlapped by button3 as you can see in the below screenshot.

Clearly, this does not look good. Isn’t it? This is happening because button3 is constrained by only button1 and is not affected by changes made on button2. To solve the above issue we need to set button3 to the right-hand side of both button1 and button3. We can achieve this by using barrier constraints.

Update the above sample code with the below-given code.
ConstraintLayout(
modifier = Modifier
.size(300.dp, 200.dp)
.padding(10.dp)
) {

val (button1, button2, button3) = createRefs()
val barrier = createEndBarrier(button1, button2)

Button(onClick = { }, modifier = Modifier
.constrainAs(button1) {
top.linkTo(parent.top)
start.linkTo(parent.start)
}
.width(100.dp)) {
Text("Button 1")
}

Button(onClick = { }, modifier = Modifier
.constrainAs(button2) {
top.linkTo(button1.bottom, margin = 20.dp)
start.linkTo(parent.start)
}
.width(180.dp)) {
Text("Button 2")
}

Button(onClick = { }, modifier = Modifier.constrainAs(button3) {
linkTo(parent.top, parent.bottom)
linkTo(button1.end, parent.end, startMargin = 10.dp)
start.linkTo(barrier, margin = 10.dp)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
}) {
Text("Button 3")
}
}

If you have any queries regarding Android app development connect with our Android app developers.

Top comments (0)