Welcome to the MAD Expertise collection on Jetpack Compose layouts and modifiers! On this first put up, we’re going to start out our journey by explaining the fundamentals of layouts and modifiers. We’ll go over how they work collectively, what out-of-the-box APIs Compose provides, and how one can fantastically fashion your UI — all this whereas constructing a display for a mini pixelated sport known as Android Aliens!
For those who haven’t checked out the earlier MAD Expertise collection on Compose fundamentals, we propose you accomplish that earlier than persevering with, as it can give you an excellent foundation to construct on prime of.
For those who’ve obtained any questions so removed from this collection on Compose Layouts and Modifiers, we can have a stay Q&A session on March ninth. Depart a remark right here, on YouTube, or utilizing #MADCompose on Twitter to ask your questions.
You can too watch this put up as a MAD Expertise video:
Layouts are the core parts of Compose UI which allow you to make beautiful apps with a wide range of supplied, ready-to-use APIs, in addition to construct your personal customized options. In Compose, you employ composable capabilities to emit parts of your UI, however layouts are those that specify the exact association and alignment of these parts.
Due to this fact, layouts, whether or not you employ those Compose gives without spending a dime or construct your personal customized ones, are a very important a part of the Compose world. You’ll be able to take a look at them as Compose coordinators — dictating the construction of different composables nested inside. In reality, we’ll see later on this collection that nearly all the pieces in Compose UI is a structure! However we’ll get to that afterward.
Now, should you’ve used Compose earlier than (⭐ for you in case you have!), you could have observed how necessary and essential modifiers are to this framework. They will let you beautify and increase composables, moulding them the way in which you want them to be and enabling them to do what you want them to do. By chaining as many modifiers as you want, you may:
Now that we’ve coated the details on layouts and modifiers, let’s see them in motion!
Let’s roll up our sleeves and construct a enjoyable sport display to know how layouts and modifiers work collectively. We’ll begin off by utilizing these two Android Alien ships and progressively construct as much as a full screenshot of the sport:
So we’d like two ships! The reusable AndroidAlien
composable seems to be like this:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAlien(
coloration: Shade,
modifier: Modifier = Modifier,
) {
Picture(
modifier = modifier,
painter = painterResource(R.drawable.android_alien),
colorFilter = ColorFilter.tint(coloration = coloration),
contentDescription = null
)
}
Then, calling this composable twice, we add a inexperienced and a pink alien to the dad or mum AndroidAliens
:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliens() {
AndroidAlien(
coloration = Shade.Pink,
modifier = Modifier
.dimension(70.dp)
.padding(4.dp)
)
AndroidAlien(
coloration = Shade.Inexperienced,
modifier = Modifier
.dimension(70.dp)
.padding(4.dp)
)
}
Two issues to elucidate right here on the modifier utilization when constructing easy composables like these:
- Within the first snippet, we set a
modifier
parameter with a default argument, as per Compose API greatest practices - Within the second snippet, we cross a modifier chain consisting of two quite common modifiers to set the dimension and padding of the picture
Simply studying the final code pattern, we would need (and count on) Compose to put these baby composables out in a particular order. Nevertheless, once we run it, we simply see one ingredient on the display:
The pink ship appears to be lacking. To debug this, let’s enhance the dimensions of the pink ship:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliens() {
AndroidAlien(
coloration = Shade.Pink,
modifier = Modifier.dimension(100.dp)
)
// …
}
The pink ship appears a bit shy and is hiding behind the inexperienced one 🤔. Which means, these two composables are in reality overlapping one another, as they’re lacking particular directions on how one can be laid out. Compose can do a number of issues, however it can not learn your thoughts! This tells us that our ships want some construction and formations, and that’s exactly what Compose layouts do.
Relatively than having our alien ships utterly overlapping one another, we wish them to be laid out one subsequent to one another — a quite common use case for UI parts in Android apps basically. We’d like our ships to be positioned facet by facet, in addition to one above the opposite:
To do that, Compose provides Column
and Row
structure composables, for laying out parts vertically and horizontally, respectively.
Let’s first transfer our two AlienShips
inside a Row
to put them out in a horizontal formation:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensRow() {
Row {
AndroidAlien(…)
AndroidAlien(…)
}
}
Conversely, to rearrange gadgets vertically, wrap your ships in a Column
composable:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensColumn() {
Column {
AlienShip(...)
AlienShip(...)
}
}
Now, this seems to be fantastic however we wish to tweak their positioning a bit extra. We wish them to be aligned on our display, like this:
We wish the spaceships to be “glued” to a particular nook of our display, like the underside finish. This implies we have to align and prepare these ships accordingly, whereas nonetheless preserving them in Column
and Row
formations.
To specify such exact positioning of parts inside your Column
and Row
mother and father, you should use Association
s snd Alignment
s. These properties instruct layouts on how one can place its kids. Which means, should you wished your nested ships to be “glued” to the underside finish of the Column
dad or mum, you set the next:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensRow() {
Row(
horizontalArrangement = Association.Middle,
verticalAlignment = Alignment.Prime,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensColumn() {
Column(
verticalArrangement = Association.Prime,
horizontalAlignment = Alignment.CenterHorizontally,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
Nevertheless, once we run the app, we are able to nonetheless see our alien ships as they have been beforehand:
We will’t glue them to the nook as a result of layouts like Column
and Row
wrap across the dimension of their kids and don’t transcend that, so we can not see the results of the set Preparations
and Alignments
. To have the Column
increase throughout the whole obtainable dimension, we’ll use the .fillMaxSize()
modifier, which has a really self-explanatory identify:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensRow() {
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Association.Middle,
verticalAlignment = Alignment.Prime,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensColumn() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Association.Prime,
horizontalAlignment = Alignment.CenterHorizontally,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
That does the trick! Discover how Column
makes use of a vertical association and horizontal alignment, whereas Row
accepts the horizontal association and vertical alignment. Why is that?! Let’s clarify the distinction between the 2 in additional element.
Association
refers to how the structure kids are laid out on the predominant axis — vertical for Column
and horizontal for Row
. Alignment does the identical, however on the cross axis — horizontal for Column
and vertical for Row:
There are a selection of properties to select from and alter your composables with. You’ll be able to even area out your aliens in numerous methods in order that they aren’t too cozy with one another. Check out the documentation and discover all choices which may fit your design necessities.
Aligning and arranging parts in layouts in such a approach ensures that the identical guidelines are utilized to all kids. However what occurs once we desire a third, rogue alien ship to break free from the imposed guidelines? Let’s break down the precise positioning — we wish the inexperienced and blue aliens to comply with the dad or mum rule directions, however we wish the pink alien to insurgent and “escape”:
To attain this, Compose provides the .align()
modifier for use on the particular baby composable that you just want to place individually, defying the foundations enforced by the dad or mum:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensRow() {
Row(…) {
AndroidAlien(…)
AndroidAlien(…)
AndroidAlien(…)
AndroidAlien(
coloration = Shade.Pink,
modifier = Modifier.align(Association.CenterVertically)
) // Rogue Revolt ship
}
}
Let’s proceed with our sport construct up and see what occurs when a huge alien mothership enters the sector!
Because the identify hints, we wish the mothership to be bigger than the remainder of its alien topics. On this alien row formation, the common ships ought to occupy the minimal width they should render accurately and the pink mothership ought to occupy the remaining width of the row:
This can be a frequent use case the place you need solely a few of your baby UI parts to have a larger or smaller dimension than the remaining. You can manually change the dimensions of simply that one ingredient, however that could be cumbersome and you may want the dimensions to be relative to the remainder of the weather, slightly than a static, mounted worth. For this, Column
and Row
composables provide the .weight()
modifier:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensRow() {
Row(…) {
AndroidAlien(modifier = Modifier.dimension(70.dp)) // Takes precisely 70 DP
AndroidAlien(modifier = Modifier.weight(1F)) // Will get the remaining
AndroidAlien(modifier = Modifier.dimension(70.dp)) // Takes precisely 70 DP
}
}
The mothership has utterly hogged the sport area! Not assigning weights to the opposite parts implies that they’d solely occupy the width (in Row
) or top (in Column
) as they require, and the remaining width/top is equally distributed to the weather which have assigned weights.
However what if we wish the opposite, common ships to occupy exactly 1/4 of the width and the mothership 2/4? Then, we are able to assign weights within the following method:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensRow() {
Row(…) {
AndroidAlien(modifier = Modifier.weight(1F))
AndroidAlien(modifier = Modifier.weight(2F))
AndroidAlien(modifier = Modifier.weight(1F))
}
}
You would possibly discover the common alien ships are actually bigger as nicely. The default behaviour of assigning weights is to resize the merchandise as nicely, to suit the newly assigned width/top. Nevertheless, should you want to hold a component in its unique dimension, you may cross false to the fill
parameter of this modifier:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */AndroidAlien(
modifier = Modifier
.dimension(70.dp)
.weight(1F, fill = false)
)
Now that we’ve coated how one can lay out parts vertically and horizontally with Compose’s easy, however highly effective Columns
and Rows
, in addition to some useful modifier to make your UI tremendous neat, let’s transfer on!
An necessary function of our sport is to let you recognize when it’s GAME OVER 😢. Let’s take a look at the design the place you’d have to show this overlay on prime of the alien formation:
You’ll be able to see this requires stacking the textual content on prime of the aliens, to make it tremendous clear to you that the sport is over. In Compose, you should use the Field
structure as a fast approach of placing parts on prime of one another or overlapping them, following the order the composables are executed in:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensGameOverBox() {
Field {
AndroidAliensRow(…)
Textual content(
textual content = “GAME OVER”
// …
)
}
}
Equally to our earlier layouts, the Field
composable additionally provides a approach of instructing it how one can lay out its nested kids. The distinction right here is that there’s no differentiation between horizontal and vertical — as a substitute, they’re merged into one contentAlignment
:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensGameOverBox() {
Field(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Middle
) {
AndroidAliensRow(…)
Textual content(
textual content = “GAME OVER”
// …
)
}
}
Within the earlier code snippet, we used contentAlignment = Alignment.Middle
to align baby parts within the middle, nonetheless you would additionally use any of the present 2D alignments. This makes Field
structure very helpful whenever you want to place the nested kids, nicely, just about wherever you need inside it!
If the sport is certainly over, your predominant sport display would possibly look nicer with an overlaid clear background to make it much more obvious that you just can not play anymore:
If solely there was a method to set a grey, clear background behind the “GAME OVER” Textual content
and increase it to cowl the obtainable dimension 🤔… And there’s!
Field
structure gives a useful .matchParentSize()
modifier that enables a toddler ingredient to match the dimensions of the containing Field
:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensGameOverBox() {
Field(…) {
AndroidAliensRow(…)
Spacer(
modifier = Modifier
.matchParentSize()
.background(coloration = Shade.Grey.copy(alpha = .7f))
)
Textual content(…)
}
}
Be mindful nonetheless, that this baby ingredient doesn’t participate in defining the ultimate dimension of the Field
dad or mum. As an alternative, it matches the dimensions of the Field
in any case different kids (not utilizing matchParentSize()
modifier) have been measured first to acquire the total Field’s
dimension. In distinction, the .fillMaxSize()
modifier, which makes a component occupy all obtainable area, will participate in defining the dimensions of the Field
.
🚨 You would possibly discover that matchParentSize
is barely obtainable inside a Field
composable. Why and the way? This leads us to a vital modifier idea in Compose: scope security.
In Compose, there are modifiers that may solely be used when utilized to kids of sure composables. Compose enforces this via customized scopes. That’s how matchParentSize
is barely obtainable in BoxScope
and weight
in ColumnScope
and RowScope
. This prevents you from including modifiers that merely received’t work somewhere else and saves you time from trial and error.
Our mini sport may undoubtedly profit from some info on the sport progress whereas we play — like a header with the present rating and a button on the backside to start out or pause the sport, which wrap the remainder of the sport content material in between:
This would possibly remind you of getting one thing like a prime and backside bar, the place our header and button can be positioned in. To implement this, Compose provides a really useful set of out-of-the-box composables to make use of. And so we come to Materials parts!
Jetpack Compose provides an implementation of Materials Design, a complete design system for creating digital interfaces. Materials parts, equivalent to buttons, playing cards, switches, in addition to layouts like Scaffold
, can be found as composable capabilities. Collectively, they signify interactive constructing blocks for making a consumer interface. There are lots of to select from, so make certain to take a look at the Compose Materials API reference for particulars.
In our case of including a header as a prime and a button as a backside bar, Compose and Materials present the Scaffold
structure, which comprises slots for numerous parts to be laid out into frequent display patterns. So let’s add our begin button and rating header as prime and backside bars:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensWithInfo() {
Scaffold(
topBar = {
InfoHeader(…)
},
bottomBar = {
Button(…) {
Textual content(
textual content = “PRESS START”
)
}
}
) {
AndroidAliens(…)
}
}
Different frequent use instances for the highest and backside bar are the evergreen toolbar and the underside navigation bar. However because the Scaffold
composable parameters settle for generic composable lambdas, you may cross in any sort of a composable you’d like:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable Scaffold(
// …
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
// …
)
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensWithInfo() {
Scaffold(
topBar = {
ComposeShipsRow(…)
},
bottomBar = {
AndroidAliensRow(…)
}
) {
AndroidAliens(…)
}
}
This open slotted idea known as Slot API and is closely used throughout Compose. Scaffold
additionally accepts floating motion buttons, snackbars, and lots of different customization choices.
Now, you would possibly discover we’ve used a Button
composable as the underside bar. This is only one extra of many out-of-the-box options that Materials parts provide. It permits a fast approach of including a button to your display and offering any form of content material inside it — textual content, photos and others, thus additionally utilizing the Slot API idea. For studying extra about the whole Materials portfolio, try the Materials parts documentation.
Improbable! Our sport is progressing rather well. However as with our sport, our Compose tutorial additionally will increase the issue with every stage. So let’s transfer onto the following problem.
Thus far, we’ve had a really restricted quantity of alien ships on the display in a easy SVG type. However what if we had tons of or 1000’s even? And what in the event that they’re animated?!
This little invasion may trigger some severe jank in that case. Loading up all your content material without delay from the backend, particularly if it comprises giant knowledge units, heavy photos or movies, can influence your app’s efficiency. What if as a substitute you would load your content material on demand, little by little when scrolling? Cutscene to my private favorites, the Lazy parts in Compose.
Lazy lists render a scrollable record of things as they turn out to be seen on the display, slightly than . To construct an impressively fast grid of inexperienced Android Aliens, we’ll use the LazyVerticalGrid
composable, then set a hard and fast quantity of columns on it and add 50 AndroidAlien
gadgets to it:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */@Composable
enjoyable AndroidAliensGrid() {
// Specify that the grid ought to have 5 columns exactly
LazyVerticalGrid(columns = GridCells.Fastened(5)) {
// Add 50 gadgets or inexperienced Android aliens
gadgets(50) {
AndroidAlien(…)
}
}
}
There’s quite a lot of further Lazy parts to select from: LazyHorizontalGrid
, LazyColumn
, LazyRow
and most not too long ago, staggered grids — LazyVerticalStaggeredGrid
and LazyHorizontalStaggeredGrid
.
Lazy layouts are an enormous Compose space and will simply benefit a whole put up for themselves, so to get the greatest and most exhaustive info on this API, try the Lazy layouts in Compose video the place we clarify all the pieces there’s to find out about Lazy layouts.
We’ve got coated A LOT immediately! From the very fundamentals on Compose layouts and modifiers and their mutual collaboration, what out-of-the-box APIs are supplied, Materials parts and Slot APIs, in addition to how one can add content material on demand — all that whereas constructing a sport display of our personal.
Keep tuned for the following put up the place we cowl phases of Compose below the hood. And naturally, don’t neglect to subscribe for normal updates!
Primary layouts in Compose codelab: https://developer.android.com/codelabs/jetpack-compose-layouts
Primary layouts in Compose codealong: https://www.youtube.com/watch?v=kyH01Lg4G1E