ConstraintLayout
can help place composables relative to others on the screen,
and is an alternative to using multiple nested Row
, Column
, Box
and
custom layout elements. ConstraintLayout
is useful when implementing larger
layouts with more complicated alignment requirements.
To use ConstraintLayout
in Compose, you need to add this dependency in your
build.gradle
:
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02"
ConstraintLayout
in Compose works with a DSL:
- References are created using
createRefs()
orcreateRefFor()
, and each composable inConstraintLayout
needs to have a reference associated with it. - Constraints are provided using the
constrainAs()
modifier, which takes the reference as a parameter and lets you specify its constraints in the body lambda. - Constraints are specified using
linkTo()
or other helpful methods. parent
is an existing reference that can be used to specify constraints towards theConstraintLayout
composable itself.
Here's an example of a composable using a ConstraintLayout
:
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout {
// Create references for the composables to constrain
val (button, text) = createRefs()
Button(
onClick = { /* Do something */ },
// Assign reference "button" to the Button composable
// and constrain it to the top of the ConstraintLayout
modifier = Modifier.constrainAs(button) {
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text("Button")
}
// Assign reference "text" to the Text composable
// and constrain it to the bottom of the Button composable
Text("Text", Modifier.constrainAs(text) {
top.linkTo(button.bottom, margin = 16.dp)
})
}
}
This code constrains the top of the Button
to the parent with a margin of
16.dp
and a Text
to the bottom of the Button
also with a margin of
16.dp
.
Decoupled API
In the ConstraintLayout
example,
constraints are specified inline, with a modifier in the composable they're
applied to. However, there are situations when it's preferable to decouple the
constraints from the layouts they apply to. For example, you might want to
change the constraints based on the screen configuration, or animate between two
constraint sets.
For cases like these, you can use ConstraintLayout
in a different way:
- Pass in a
ConstraintSet
as a parameter toConstraintLayout
. - Assign references created in the
ConstraintSet
to composables using thelayoutId
modifier.
@Composable
fun DecoupledConstraintLayout() {
BoxWithConstraints {
val constraints = if (minWidth < 600.dp) {
decoupledConstraints(margin = 16.dp) // Portrait constraints
} else {
decoupledConstraints(margin = 32.dp) // Landscape constraints
}
ConstraintLayout(constraints) {
Button(
onClick = { /* Do something */ },
modifier = Modifier.layoutId("button")
) {
Text("Button")
}
Text("Text", Modifier.layoutId("text"))
}
}
}
private fun decoupledConstraints(margin: Dp): ConstraintSet {
return ConstraintSet {
val button = createRefFor("button")
val text = createRefFor("text")
constrain(button) {
top.linkTo(parent.top, margin = margin)
}
constrain(text) {
top.linkTo(button.bottom, margin)
}
}
}
Then, when you need to change the constraints, you can just pass a different
ConstraintSet
.
Learn more
Learn more about ConstraintLayout in Compose from the APIs in action in the
Compose samples that use ConstraintLayout
.