Jetpack Compose

Jetpack Compose: Layouts

Alex Zhukovich 13 min read
Jetpack Compose: Layouts
Table of Contents

Introduction

Jetpack Compose provides a declarative way of building the UI of the Android app. Layouts are an essential component for creating UI, and Jetpack Compose already supports a few of them.

This article will explore available layouts in Jetpack Compose combined with modifiers, alignments, and arrangements that help to build the UI of Android apps.

This article is a part of Jetpack Compose series:

The content of this article is based on a 1.0.0-rc02 version of Jetpack Compose.

Alignment

I would like to start with Alignment and Arrangement that help us with UI elements’ arrangements.

There are nine alignment options that can be applied to child UI elements:

TopStart TopCenter TopEnd
CenterStart Center CenterEnd
BottomStart BottomCenter BottomEnd

The alignment is used as one of the parameters for the "Box" layout. We will explore that in the "Layout" section.

Demo: Alignment as a parameter in Box layout.
Box(
    contentAlignment = Alignment.BottomEnd,
    modifier = Modifier
        .size(height = 120.dp, width = 200.dp)
        .background(Color(0xFFCA8DC4))
) {
    Box(
        modifier = Modifier
            .padding(8.dp)
            .size(80.dp)
            .background(Color(0xFF342E6C))
    ) {
        ...
    }
}

We also have a set of constants for Alignment.Horizontal and Alignment.Vertical interfaces.

Constants for the Alignment.Horizontal interface:

  • Start
  • CenterHorizontally
  • End

Constants for the Alignment.Vertical interface:

  • Top
  • CenterVertically
  • Bottom

Alignment.Horizontal and Alignment.Vertical are used as a parameter for the "Column and "Row" layouts. We will explore those in the "Layout" section.

Arrangement

Arrangement is used to specify the arrangement of child elements in "Column" and "Row" layouts in the axis direction (horizontal and vertical).
We have a set of predefined values for horizontal and vertical arrangements:

  • Horizontal
    • Start
    • End
    • Center
    • SpaceEvenly
    • SpaceBetween
    • SpaceAround
  • Vertical
    • Top
    • Bottom
    • Center
    • SpaceEvenly
    • SpaceBetween
    • SpaceAround

Let's take a look at a visual representation of basic arrangements like "Start", "End", "Center", "Top", and "Bottom".

Arrangements: Start, End, Center, Top, and Bottom.

We also have three arrangements that can be applied as vertical and horizontal arrangements:

  • SpaceEvenly
  • SpaceBetween
  • SpaceAround

The SpaceEvenly arrangement places child elements across the main axis, including free space before the first and after the last child.

Arrangement.SpaceEvenly.

The SpaceBetween arrangement places child elements across the main axis without free space before first and after the last child.

Arrangement.SpaceBetween.

The SpaceAround arrangement places child elements across the main axis with half of the free space before the first and after the last child.

Arrangement.SpaceAround.

Modifiers

If you are not familiar with a modifier, you can read more in the official documentation.

The modifier is an ordered, immutable collection of modifier elements that decorate or add behavior to Compose UI elements. For example, backgrounds, padding, and click event listeners decorate or add behavior to rows, text, or buttons.

All modifiers discussed are used for the layout-related examples.

Set the content size with modifiers

The dp (density-independent pixels) is used for almost all modifiers with sizes. The dp extension function can be applied to any Int, Float, or Double value.

// Int
val height = 100.dp
// Float
val height = 90.0f.dp
// Double
val height = 100.0.dp

We can explicitly set the height, width, and size to the UI element using one of the following modifiers:

  • height(height: Dp) sets the UI element’s height to a preferred value.
  • width(width: Dp) sets the UI element’s width to a preferred value.
  • size(size: Dp) sets the height and width of an element to a similar size value.
  • size(width: Dp, height: Dp) sets the height and with of an element.

We can combine multiple modifiers: modifierA().modifierB().

Let's use the height, width, and size modifiers in a few examples below.

Demo: height, width and size.
// Example: height = 50.dp, width = 200.dp
Box(
    modifier = Modifier
        .height(50.dp)
        .width(200.dp)
        .background(Color(0xFFCA8DC4))
) {
    ...
}

// Example: height = 100.dp, width = 100.dp
Box(
    modifier = Modifier
        .height(100.dp)
        .width(100.dp)
        .background(Color(0xFF342E6C))
) {
    ...
}

// Example: size = 100.dp
Box(
    modifier = Modifier.size(100.dp)
        .background(Color.Yellow)
) {

}

Sometimes, we want to use all the available space of a container. We can do this with one of the following modifiers:

  • fillMaxHeight() fills the height of an element with the maximum height of a parent.
  • fillMaxWidth() fills the width of an element with the maximum width of a parent.
  • fillMaxSize() fills the height and width of an element with the maximum height and width of a parent.
Demo: fillMaxWidth and height.
Box(
    modifier = Modifier
        .size(height = 100.dp, width = 200.dp)
        .background(Color(0xFFCA8DC4))
) {
    Box(
        modifier = Modifier
            .height(70.dp)
            .fillMaxWidth()
            .background(Color(0xFF342E6C))
    ) {
        ...
    }
}

The "align" modifier

The align modifier allows us to set arrangement of children inside the Box Scope.

Demo: the "align" modifier.
Box(
    modifier = Modifier.size(height = 120.dp, width = 300.dp)
) {
    Text(text = "TopStart", modifier = Modifier.align(Alignment.TopStart))
    Text(text = "TopCenter", modifier = Modifier.align(Alignment.TopCenter))
    Text(text = "TopEnd", modifier = Modifier.align(Alignment.TopEnd))

    Text(text = "CenterStart", modifier = Modifier.align(Alignment.CenterStart))
    Text(text = "Center", modifier = Modifier.align(Alignment.Center))
    Text(text = "CenterEnd", modifier = Modifier.align(Alignment.CenterEnd))

    Text(text = "BottomStart", modifier = Modifier.align(Alignment.BottomStart))
    Text(text = "BottomCenter", modifier = Modifier.align(Alignment.BottomCenter))
    Text(text = "BottomEnd", modifier = Modifier.align(Alignment.BottomEnd))
}

The "padding" modifier

The padding modifier allows us to add space around an element. We have three different options for adding a padding around an element:

  • padding(start: Dp = 0.dp, top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp)
  • padding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp)
  • padding(all: Dp)

As you can see, different modifiers allow us to write less boilerplate code when we need to set similar padding around the elements or set the different horizontal and vertical paddings.

Demo: padding(horizontal, vertical).
Box(
    modifier = Modifier
        .size(height = 120.dp, width = 200.dp)
        .background(Color(0xFFCA8DC4))
) {
    Box(
        modifier = Modifier
            .padding(horizontal = 16.dp, vertical = 8.dp)
            .fillMaxSize()
            .background(Color(0xFF342E6C))
    ) {
        ...
    }
}

Layouts

Building the UI of a modern mobile application, we need to combine multiple UI elements, and multiple available layouts help us with that.

Jetpack Compose supports the following layouts:

  • Box
  • Column
  • Row
  • ConstraintLayout
  • Scaffold

The "Box" layout

The "Box" layout stacks every child on top of each other. By default, the children will be placed at the Alignment.TopStart position.

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -> Unit
)

Examples:

Example 1: the "Box" layout.
Box(
    modifier = Modifier.size(height = 120.dp, width = 300.dp)
) {
    Text(
        text = "Very important text", 
        fontSize = 20.sp
    )
    Text(
        text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 
        fontSize = 20.sp
    )
}

We can use the align modifier to set alignment for children elements.

Example 2: the "Box" layout.
Box(
    modifier = Modifier.size(height = 120.dp, width = 300.dp)
) {
    Text(text = "TopStart", modifier = Modifier.align(Alignment.TopStart))
    Text(text = "Center", modifier = Modifier.align(Alignment.Center))
    Text(text = "BottomEnd", modifier = Modifier.align(Alignment.BottomEnd))
}

The "Column" layout

The "Column" layout stacks children in a vertical sequence. Comparing this with available layouts in Android development, the closest one is a LinearLayout with vertical orientation. All child items will be arranged vertically at Top and aligned horizontally at Start, by default.

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    content: @Composable ColumnScope.() -> Unit
) 

Examples:

Example 1: the "Column" layout.
Column(
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()
) {
    Image(
        bitmap = ImageBitmap.imageResource(R.drawable.espresso_small),
        contentDescription = "Espresso"
    )
    Text(
        text = "Espresso",
        fontSize = 50.sp
    )
}

We can add different weights to children inside the "Column" layout by using the weight(weight: Float) modifier. Let's take a look at an example of when different weight can be helpful:

Example 2: the "Column" layout.
Column(modifier = Modifier.height(200.dp)){
    Text(
        text = "Very important text",
        fontSize = 20.sp,
        modifier = Modifier
            .fillMaxWidth()
            .weight(2f)
            .background(Color.White)
    )
    Text(
        text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
        fontSize = 20.sp,
        modifier = Modifier
            .fillMaxWidth()
            .weight(1f)
            .background(Color.LightGray)
    )
}

The "Row" layout

The "Row" layout stacks children in a horizontal sequence. Comparing it with available layouts in Android development, the closest one is a LinearLayout with a horizontal orientation. All child items will be aligned vertically at Top and arranged horizontally at Start, by default.

@Composable
inline fun Row(
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    verticalAlignment: Alignment.Vertical = Alignment.Top,
    content: @Composable RowScope.() -> Unit
)

Examples:

Example 1: the "Row" layout.
Row(
    verticalAlignment = Alignment.CenterVertically,
    modifier = Modifier.fillMaxWidth()
) {
    Image(
        bitmap = ImageBitmap.imageResource(R.drawable.espresso_small),
        contentDescription = "Espresso",
        modifier = Modifier.size(100.dp)
    )
    Text(
        text = "Espresso",
        fontSize = 30.sp
    )
}

We can add different weights to children inside the "Column" layout using the weight(weight: Float) modifier. Let's take a look at an example of when different weight can be helpful:

Example 2: the "Row" layout.
Row(modifier = Modifier.height(200.dp)){
    Text(
        text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
        fontSize = 20.sp,
        modifier = Modifier
            .fillMaxWidth()
            .weight(3f)
            .background(Color.White)
    )
    Text(
        text = "Very important text",
        fontSize = 20.sp,
        modifier = Modifier
            .fillMaxWidth()
            .weight(1f)
            .background(Color.LightGray)
    )
}

Let's combine "Column" and "Row" layouts in one example.

Example: "Column" and "Row" layouts.
Row(
    verticalAlignment = Alignment.CenterVertically,
    modifier = Modifier.fillMaxWidth()
) {
    Image(
        bitmap = ImageBitmap.imageResource(R.drawable.espresso_small),
        contentDescription = "Espresso",
        modifier = Modifier.size(100.dp)
    )
    Column(
        modifier = Modifier.padding(horizontal = 8.dp)
    ) {
        Text(
            text = "Espresso",
            fontSize = 24.sp
        )
        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.",
            style = TextStyle(textAlign = TextAlign.Justify)
        )
    }
}

The "ConstraintLayout" layout

The "ConstraintLayout" layout positions children according to the constraints between them. It's similar to the available "ConstraintLayout" for Android development.

@Composable
fun ConstraintLayout(
    modifier: Modifier = Modifier,
    content: @Composable ConstraintLayoutScope.() -> Unit
)

Let's try to rebuild the sample with a coffee card using "ConstraintLayout" instead of a combination of "Column" and "Row" layouts.

We can define constraints use the constrainAs modifier.

We can define constraints

Example: the "ConstraintLayout" layout.
ConstraintLayout(
    modifier = Modifier.fillMaxWidth()
) {
    val (logo, title, description) = createRefs()

    Image(
        bitmap = ImageBitmap.imageResource(R.drawable.espresso_small),
        contentDescription = "Espresso",
        modifier = Modifier
            .size(100.dp)
            .constrainAs(logo) {
                top.linkTo(parent.top)
            }
    )
    Text(
        text = "Espresso",
        fontSize = 24.sp,
        modifier = Modifier.constrainAs(title) {
            top.linkTo(parent.top)
            linkTo(start = logo.end, end = parent.end, startMargin = 8.dp, endMargin = 8.dp)
            width = Dimension.fillToConstraints
        }
    )
    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.",
        style = TextStyle(textAlign = TextAlign.Justify),
        modifier = Modifier.constrainAs(description) {
            top.linkTo(title.bottom)
            linkTo(start = title.start, end = title.end)
            width = Dimension.fillToConstraints
        }
    )
}

The "Scaffold" layout

The "Scaffold" layout contains a basic implementation of the material design app structure with the following components:

  • TopBar
  • BottomBar
  • FloatingActionButton
  • Drawer
@Composable
fun Scaffold(
    modifier: Modifier = Modifier,
    scaffoldState: ScaffoldState = rememberScaffoldState(),
    topBar: @Composable () -> Unit = emptyContent(),
    bottomBar: @Composable () -> Unit = emptyContent(),
    snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
    floatingActionButton: @Composable () -> Unit = emptyContent(),
    floatingActionButtonPosition: FabPosition = FabPosition.End,
    isFloatingActionButtonDocked: Boolean = false,
    drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
    drawerGesturesEnabled: Boolean = true,
    drawerShape: Shape = MaterialTheme.shapes.large,
    drawerElevation: Dp = DrawerDefaults.Elevation,
    drawerBackgroundColor: Color = MaterialTheme.colors.surface,
    drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
    drawerScrimColor: Color = DrawerDefaults.scrimColor,
    backgroundColor: Color = MaterialTheme.colors.background,
    contentColor: Color = contentColorFor(backgroundColor),
    content: @Composable (PaddingValues) -> Unit
)

The scaffoldState: ScaffoldState provides a state for the Scaffold composable function.

@Stable
class ScaffoldState(
    val drawerState: DrawerState,
    val snackbarHostState: SnackbarHostState
)
  • The drawerState contains the state of the drawer. It can have the DrawerValue.Open or DrawerValueClosed values.
  • The snackbarHostState returns an instance of SnackbarHostState to show a snackbar inside the Scaffold.

Let's build a few simple layouts with the "Scaffold" layout:

  • Layout with "TopBar" and content area
  • Layout with "TopBar", "BottomBar", "FloatingActionButton" and content area

The layout with "TopBar" and content area

Example 1: the "Scaffold" layout.
Scaffold(
    topBar = {
        TopAppBar(title = { Text(text = "Title") })
    },
    content = {
        Text(
            text = "Content",
            fontSize = 40.sp
        )
    }
)

The layout with "TopBar", "BottomBar", "Drawer", "FloatingActionButton" and content area

Example 2: the "Scaffold" layout.

Let's start with the data class for combining information about navigation items; it includes a title and an icon. This data class helps us to store information for the bottom menu and navigation drawer.

data class NavigationItem(
    val title: String,
    @DrawableRes val iconRes: Int
)

Notes:

  • A composable function recomposes after any changes of state. To remember the current state which can be recalculated and trigger recomposition we can use the remember function. So, to create a state for selected item need to use the following code:
    val selectedItem = remember { mutableStateOf("Initial state") }
  • To change the drawerState ("open" or "close" navigation drawer) we need to use drawerState.open() or drawerState.close() functions which can be called from the CoroutineContext. So, we need to use the following code to create a CoroutineContext:
    val scope = rememberCoroutineScope()
val bottomMenuItems = listOf(
    NavigationItem(title = "Item 1", iconRes = R.drawable.ic_one),
    NavigationItem(title = "Item 2", iconRes = R.drawable.ic_two),
    NavigationItem(title = "Item 3", iconRes = R.drawable.ic_three)
)

val drawerMenuItems = listOf(
    NavigationItem(title = "Section 1", iconRes = R.drawable.ic_one),
    NavigationItem(title = "Section 2", iconRes = R.drawable.ic_two),
    NavigationItem(title = "Section 3", iconRes = R.drawable.ic_three)
)

val selectedItem = remember { mutableStateOf(bottomMenuItems.first().title) }

/**
 * By default, drawer state is closed, but we can customize it.
 *
 * Closed drawer state: `rememberScaffoldState(drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)`
 * Opened drawer state: `rememberScaffoldState(drawerState = rememberDrawerState(initialValue = DrawerValue.Open))`
 */
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()

Scaffold(
    scaffoldState = scaffoldState,
    drawerContent = {
        LazyColumn {
            items(drawerMenuItems) {
                ListItem(
                    text = { Text(text = "Category ${it.title}") },
                    icon = {
                        Icon(
                            painter = painterResource(id = it.iconRes),
                            contentDescription = it.title
                        )
                    }
                )
            }
        }
    },
    topBar = {
        TopAppBar(
            title = { Text(text = "Title") },
            navigationIcon = {
                IconButton(
                    onClick = {
                        scope.launch { scaffoldState.drawerState.open() }
                    }
                ) {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_menu),
                        contentDescription = "Menu"
                    )
                }
            }
        )
    },
    content = {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier.fillMaxSize()
        ) {
            Text(
                text = selectedItem.value,
                fontSize = 40.sp,
                textAlign = TextAlign.Center
            )
        }
    },
    bottomBar = {
        BottomNavigation {
            bottomMenuItems.forEach { screen ->
                BottomNavigationItem(
                    selected = screen.title == selectedItem.value,
                    onClick = { selectedItem.value = screen.title },
                    label = { Text(screen.title) },
                    icon = {
                        Icon(
                            painter = painterResource(id = screen.iconRes),
                            contentDescription = null
                        )
                    }
                )
            }
        }
    },
    floatingActionButtonPosition = FabPosition.End,
    floatingActionButton = {
        FloatingActionButton(
            onClick = {
                scope.launch {
                    scaffoldState.snackbarHostState
                        .showSnackbar("Button clicked")
                }
            }
        ) {
            Icon(
                painter = painterResource(id = R.drawable.ic_add),
                contentDescription = "Add",
                tint = Color.White
            )
        }
    }
)

The "BackdropScaffold" layout

The "BackdropScaffold" layout contains a basic implementation of the material design backdrop.

The BackdropScaffold includes few components

  • AppBar
  • Back Layer Content
  • Front Layer Content
Layers of "BackdropScaffold" layout.
fun BackdropScaffold(
    appBar: @Composable () -> Unit,
    backLayerContent: @Composable () -> Unit,
    frontLayerContent: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    scaffoldState: BackdropScaffoldState = rememberBackdropScaffoldState(Concealed),
    gesturesEnabled: Boolean = true,
    peekHeight: Dp = BackdropScaffoldDefaults.PeekHeight,
    headerHeight: Dp = BackdropScaffoldDefaults.HeaderHeight,
    persistentAppBar: Boolean = true,
    stickyFrontLayer: Boolean = true,
    backLayerBackgroundColor: Color = MaterialTheme.colors.primary,
    backLayerContentColor: Color = contentColorFor(backLayerBackgroundColor),
    frontLayerShape: Shape = BackdropScaffoldDefaults.frontLayerShape,
    frontLayerElevation: Dp = BackdropScaffoldDefaults.FrontLayerElevation,
    frontLayerBackgroundColor: Color = MaterialTheme.colors.surface,
    frontLayerContentColor: Color = contentColorFor(frontLayerBackgroundColor),
    frontLayerScrimColor: Color = BackdropScaffoldDefaults.frontLayerScrimColor,
    snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) }
)

The scaffoldState: BackdropScaffoldState provides a state for the BackdropScaffold composable function.

@ExperimentalMaterialApi
@Stable
class BackdropScaffoldState(
    initialValue: BackdropValue,
    animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
    confirmStateChange: (BackdropValue) -> Boolean = { true },
    val snackbarHostState: SnackbarHostState = SnackbarHostState()
) : SwipeableState<BackdropValue>(
    initialValue = initialValue,
    animationSpec = animationSpec,
    confirmStateChange = confirmStateChange
) {
   ...
}

The currentValue: BackdropValue of the BackdropScaffoldState can be represented by one of the following values

  • The Concealed indicates the back layer is concealed and the front layer is active.
  • The Revealed indicates the back layer is revealed and the front layer is inactive.

Let's build a demo application with the BackdropScaffold composable function.

Example: the "BackdropScaffold" layout.

Notes:

  • A composable function recomposes after any changes of state. To remember the current state which can be recalculated and trigger recomposition we can use the remember function. So, to create a state for selected item need to use the following code:
    val selectedItem = remember { mutableStateOf("Initial state") }
  • To change the scaffoldState we need to use scaffoldState.reveal() or caffoldState.conceal() functions which can be called from the CoroutineContext. So, we need to use the following code to create a CoroutineContext:
    val scope = rememberCoroutineScope()
val scope = rememberCoroutineScope()

val menuItems = listOf(
    "Item 1",
    "Item 2",
    "Item 3",
    "Item 4",
    "Item 5",
    "Item 6",
    "Item 7",
    "Item 8",
    "Item 9",
    "Item 10"
)
val selectedItem = remember { mutableStateOf(menuItems.first()) }
val scaffoldState = rememberBackdropScaffoldState(BackdropValue.Concealed)

BackdropScaffold(
    scaffoldState = scaffoldState,
    appBar = {
        TopAppBar(
            title = { Text("Title") },
            navigationIcon = {
                if (scaffoldState.isConcealed) {
                    IconButton(onClick = { scope.launch { scaffoldState.reveal() } }) {
                        Icon(
                            painter = painterResource(id = R.drawable.ic_menu),
                            contentDescription = "Open menu"
                        )
                    }
                } else {
                    IconButton(onClick = { scope.launch { scaffoldState.conceal() } }) {
                        Icon(
                            painter = painterResource(id = R.drawable.ic_arrow_back),
                            contentDescription = "Close menu"
                        )
                    }
                }
            },
            elevation = 0.dp,
            backgroundColor = Color.Transparent
        )
    },
    backLayerContent = {
        LazyColumn(
            modifier = Modifier.fillMaxSize()
        ) {
            items(menuItems) {
                ListItem(
                    text = { Text(text = it) },
                    modifier = Modifier.clickable {
                        selectedItem.value = it
                        scope.launch { scaffoldState.conceal() }
                    }
                )
            }
        }
    },
    frontLayerContent = {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier.fillMaxSize()
        ) {
            Text(
                text = selectedItem.value,
                fontSize = 40.sp,
                textAlign = TextAlign.Center
            )
        }
    }
)

Summary

Jetpack Compose provides possibilities for building complex layouts for Android applications using modifiers, alignments, arrangements, and layouts.

We explored only modifiers for setting the size of UI elements:

  • The height(height: Dp) sets a UI element’s height to the preferred value.
  • The width(width: Dp) sets a UI element’s width to the preferred value.
  • The size(size: Dp) sets an element’s height and width to a similar size value.
  • The fillMaxHeight() fills the height of an element with the maximum height of a parent.
  • The fillMaxWidth() fills the width of an element with a parent’s maximum width.
  • The fillMaxSize() fills the height and width of an element with a parent’s maximum height and width.

Alignments and arrangements help us to position child elements inside the parent container.

The following layouts are available in Jetpack Compose:

  • The "Box" layout stacks every child on top of each other.
  • The "Column" layout stacks children in a vertical sequence.
  • The "Row" layout stacks children in a horizontal sequence.
  • The "ConstraintLayout" layout positions children according to the constraints between them.
  • The "Scaffold" layout contains a basic implementation of the material design app structure.
  • The "BackdropScaffold" layout contains a basic implementation of the material design backdrop.

Source code of all examples in this article.


Do not hesitate to ping me on Twitter if you have any questions.


Mobile development with Alex

A blog about Android development & testing, Best Practices, Tips and Tricks

Share
More from Mobile development with Alex
Jetpack Compose: Divider
Jetpack Compose

Jetpack Compose: Divider

This article covers using and customizing the “Dividers” components from the "Material 2" and "Material 3" libraries in the Jetpack Compose. In addition to that, we will explore the difference between implementation of the Divider, HorizontalDivider and VerticalDivider.
Alex Zhukovich 4 min read
Jetpack Compose: Switch
Jetpack Compose

Jetpack Compose: Switch

This article covers creating and customizing the "Switch" component in Jetpack Compose for enabling/disabling features. It explores differences between "Material" and "Material 3" libraries, and how to interact with and verify the Switch component's state in UI tests.
Alex Zhukovich 8 min read

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Mobile development with Alex.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.