Build tailor made layouts for Android apps with Jetpack Compose


Web Development

Layouts can be explained as containers that residence the Watch objects — this kind of as buttons, textual content fields, photographs, and extra — that we see on an app UI. They outline how views are organized and exhibited on an app UI.

Three Example Layout Types Provided By Jetpack Compose Shown Enclosed In Dashed Line Boxes: Three Vertical Views (Left), Three Horizontal Views (Center), Three Views Arranged One On Left And Two Stacked On Right (Right)

Jetpack Compose, Android’s modern day UI toolkit, delivers some prevalent structure sorts for developers to use. Nonetheless, you can also use Jetpack Compose to generate custom made layouts primarily based on your needs.

Let’s study extra about personalized layouts for Android applications and how to build them employing Jsatisetpack Compose. In this short article:

Why you need to know how to build custom made layouts

Jetpack Compose delivers numerous developer resources for creating quicker Android applications, which include a variety of structure solutions. Sometimes you can implement the structure necessities for an app’s UI utilizing these current layouts in Jetpack Compose.

Having said that, these existing layouts do not normally fulfill job layout necessities. In these types of cases, you need to know how to develop a personalized structure to fit your project’s correct requirements.

Overview of layouts in Jetpack Compose

Some common layouts in Jetpack Compose are:

  • Box: a format that places its sights on best of yet another
  • Column: a structure that locations its sights in a vertical sequence
  • Row: a format that spots its sights in a horizontal sequence
  • ConstraintLayout: a structure that places its sights relative to some others

Not too long ago, LazyVerticalGrid and LazyHorizontalGrid, which had been grid layouts below tests, ended up absolutely introduced.

Along with this update came an interesting new structure named LazyLayout. This is a layout that only composes and lays out currently needed products — in other terms, goods that can be visible on a device display screen at a place in time.

You can use lazy layouts to establish effective scrollable layouts. LazyLayout versions include:

  • LazyList, which displays scrollable lists in:
  • LazyGrid, which shows scrollable grids in:

I know you have observed the term “lazy” a ton, and no, it does not mean these layouts are unwilling to accomplish their functions (like some of us 🙃). As an alternative, it basically indicates a lazy structure will carry out its functionality only when required. In other words and phrases, it is truly efficient.

This effectiveness is why lazy layouts are utilised for layouts that intend on displaying huge quantity of sights, allowing for them to be quickly organized and scrollable in the sort of lists and grids.

Measures to create a custom layout in Jetpack Compose

For you to correctly fully grasp the system of developing your individual layout, I will use a simple instance. We will develop a format I like to phone a ReverseFlowRow.

This format basically areas its sights future to each and every other, transferring on to the following line when the current line is stuffed up. Even so, it starts arranging its sights from the stop place to the commence situation of the screen — in other words and phrases, from right to remaining:

Basic Custom Android App Layout Called ReverseFlowRow Showing Five Labeled Views Aka Composables Arranged From Right To Left And Stacked, Three On Top, Two On Bottom

These kinds of a layout is what I truly feel ought to be utilised for Jetpack Compose’s AlertDialog buttons to satisfy Materials Style recommendations.

At this time, a identical layout is becoming made use of, but it goes from the get started placement to the close situation of the monitor, which does not fulfill these rules. You can obtain the concern I filed on it with IssueTracker.

Much more good posts from LogRocket:

The idea powering Jetpack Compose layouts for Android apps

To display sights on the monitor, Jetpack Compose composes the UI tree of nodes (which symbolize views), lays out each watch in the UI tree, and attracts just about every of them to the display.

For the needs of this article, we are only fascinated in the laying out of sights, because we can deal with producing a tailor made structure through this method. The course of action of laying out the sights inside of a layout transpires in 3 techniques:

  1. Measuring all sights in the format (i.e. the children)
  2. Determining what dimensions the layout ought to be
  3. Inserting the youngsters in just the boundaries of the layout

Employing the Format composable

In Jetpack Compose, laying out sights can be achieved applying the Structure composable, which is described as:

@Composable inline enjoyment Layout(
    material: @Composable @UiComposable () -> Device,
    modifier: Modifier = Modifier,
    measurePolicy: MeasurePolicy

The information parameter specifies the look at or sights (referred to as Composables) you want to be in this structure. The modifier parameter is utilized to define some modifications to the format, which can be handed from the mother or father view or composable.

The most significant section of the code previously mentioned is MeasurePolicy, which defines the measurement of baby views, the size of the structure, and the placing of the kid views in the structure.

So our ReverseFlowRow will begin off as this:

pleasurable ReverseFlowRow(
    articles: @Composable () -> Device
) = Structure(articles)  measurables, constraints ->
    // measuring young children, structure sizing, and putting children takes spot listed here.

You might see we represented MeasurePolicy as a lambda. This is doable because MeasurePolicy is a functional interface.

Also in the code over, measurables is the record of youngsters that require to be measured, whilst constraints is the layout boundaries from the father or mother.

Measuring all sights in the custom format

We measure every boy or girl with constraints by contacting evaluate(constraints) on every single of them. This returns a Placeable, which corresponds to a youngster layout that can be positioned by its father or mother structure.

val placeables =  measurable ->
    // Measure every kid.

Note that we utilized the parent’s constraints when measuring just about every youngster. This permits every boy or girl to be in a position to the full area in the mum or dad if possible.

Including dimensions constraints to the tailor made layout

Upcoming, we determine the size of the layout by calling the format() technique and specifying at least its width and top.

structure(constraints.maxWidth, constraints.maxHeight) 
   // Placement of young children happens listed here.

In this article we made use of the optimum width and peak of the parent’s constraint. Consequently, relying on the mum or dad constraints, this layout may well or could not acquire up the entire monitor.

Placing sights inside the structure

Ultimately, we area the measured children, also named Placeable elements, in the structure by contacting the placeRelative() process.

This process need to be used if you want to immediately mirror your format when the device’s layout route changes — in other terms, from left-to-proper to suitable-to-left and vice versa.

Notice that you can get the present LayoutDirection inside of the format() receiver. This can be handy if you do not want to automatically mirror your structure when a layout route variations, but rather, make your mind up how you want to position your sights in every single layout route.

If you do not want your layout to be routinely mirrored based mostly on layout route, use the location() process rather.

// Keep track of the x and y co-ordinates we have positioned small children up to.
var yPosition = 
var xPosition = constraints.maxWidth

// Put youngsters in the guardian structure.
placeables.forEach  placeable ->
    if (placeable.width < xPosition) 
        // There is still enough space in the current row to add the next child.
        xPosition -= placeable.width
        // Space left in the current row is not enough for the child. 
        // Move to the next row.
        yPosition += placeable.height
        xPosition = constraints.maxWidth - placeable.width
    // Position child on the screen.
    placeable.placeRelative(xPosition, yPosition)

As you can see, we need to keep track of the x and y coordinates used to indicate where a placement should start for each child. This will enable us to place one child next to another as well as know when to move on to the next line or row.

Final Jetpack Compose project code for custom Android app layout

Our complete layout will look like this:

fun ReverseFlowRow(
    mainAxisSpacing: Dp,
    crossAxisSpacing: Dp,
    content: @Composable () -> Unit
) = Structure(information)  measurables, constraints ->
    // 1. The measuring section.
    val placeables =  measurable ->

    // 2. The sizing stage.
    layout(constraints.maxWidth, constraints.maxHeight) 
        // 3. The placement period.
        var yPosition = 
        var xPosition = constraints.maxWidth

        placeables.forEach  placeable ->
            if (placeable.width < (xPosition + mainAxisSpacing.roundToPx())) 
                xPosition -= (placeable.width + mainAxisSpacing.roundToPx())
                yPosition += (placeable.height + crossAxisSpacing.roundToPx())
                xPosition = constraints.maxWidth - placeable.width - mainAxisSpacing.roundToPx()
            placeable.placeRelative(xPosition, yPosition)

Did you notice I added two new properties: mainAxisSpacing and crossAxisSpacing? These are used to add spacing between each child in the layout in the horizontal and vertical directions, respectively.

Testing our custom layout

To preview our layout, we can wrap it within a composable function that is annotated with @Preview. This enables us to run a sample of the layout without the extra configuration required for an Android application. Also, let’s add some text views/composables within our layout to see how it displays them:

fun ReverseFlowRowPreview() 
    ReverseFlowRow(mainAxisSpacing = 16.dp, crossAxisSpacing = 16.dp) 
        Text("First", fontSize = 20.sp, style = TextStyle(background = Color.Red))
        Text("Second", fontSize = 20.sp, style = TextStyle(background = Color.LightGray))
        Text("Third", fontSize = 20.sp, style = TextStyle(background = Color.Blue))
        Text("Fourth", fontSize = 20.sp, style = TextStyle(background = Color.Green))
        Text("Fifth", fontSize = 20.sp, style = TextStyle(background = Color.Gray))
        Text("Sixth", fontSize = 20.sp, style = TextStyle(background = Color.Yellow))
        Text("Seventh", fontSize = 20.sp, style = TextStyle(background = Color.Cyan))
        Text("Eight", fontSize = 20.sp, style = TextStyle(background = Color.Magenta))
        Text("Ninth", fontSize = 20.sp, style = TextStyle(background = Color.DarkGray))

Running the preview yields the following:

Custom ReverseFlowRow Android App Layout Filled Out With Nine Colored Text Views

Although we used text views/composables in this example, this custom Jetpack Compose layout will work with other Jetpack Compose composables as well. You can also use the concepts and follow the steps we covered in this tutorial to create your own custom layout.


This is pretty much what creating your own layout entails. This example considers a regular case scenario, like the layout required for the buttons of an AlertDialog, but it can be improved to cater to more case scenarios, such as a situation where children have different sizes.

If you use the ReverseFlowRow layout we created as is with children of different sizes, there will be some overlap amongst them. Some additional code is needed to accommodate such a case. If you would like to tackle this mini-challenge, post the updated code in the comments below!

I am excited to see your solutions. In the meantime, you can learn more about Jetpack Compose topics such as theming to improve your Android app development skills.

Leave a Reply

Your email address will not be published. Required fields are marked *