Skip to content

Compose ConstraintLayout DSL Syntax

Oscar Adame Vazquez edited this page Jan 19, 2022 · 8 revisions

A quick reference on how to implement some typical ConstraintLayout cases in Compose.

ConstraintSet DSL vs Modifier DSL

Both syntax work very much alike with a slight difference in setup.

ConstraintSet

    val constraintSet = ConstraintSet {
        val titleText = createRefFor("title")
        // Helpers are defined in this scope
        createStartBarrier(titleText)

        constrain(titleText) {
            // constraints, dimensions, transforms
        }
    }
    ConstraintLayout(constraintSet) {
        Text(text = "", modifier = Modifier.layoutId("title"))
    }

Modifier

    ConstraintLayout {
        val title = createRef()
        // Helpers are defined in this scope
        createStartBarrier(title)

        Text(text = "", modifier = Modifier.constrainAs(title) {
            // constraints, dimensions, transforms
        })
    }
  • Note that only the immediate children Composables can be constrained
  • Prefer ConstraintSet for more manageable and reusable Layouts

ConstrainScope

The available API under the ConstrainScope, ie:

constrain(createRefFor("id")) {
    // The API here
}

Constraints

Constrain a Composable to another using Anchorable.linkTo()

constrain(title) {
   start.linkTo(parent.start, margin = 10.dp)
}

Or align a Composable with center...()

constrain(title) {
   centerTo(parent)                          // Center around or inside the given reference
   centerHorizontallyTo(parent, bias = 0.5f) // Constrain start & end to the same anchors at the given reference
   centerVerticallyTo(parent, bias = 0.5f)   // Constrain top & bottom to the same anchors at the given reference
}

Dimension Behavior

Use androidx.constraintlayout.compose.Dimension to define the dimension behavior of a Composable.

constrain(title) {
   width = Dimension.value(10.dp)
}

The typical use-cases:

  • Dimension.value(Dp)
  • Dimension.wrapContent
  • Dimension.matchParent
  • Dimension.fillToConstraints - Equivalent to "match_constraints" in the View system

See the Dimension interface documentation for more.

Ratio

You can use Dimension.ratio(String) to define a dimension as a proportion to the other.

Where the String is a Width:Height ratio.

   height = Dimension.value(10.dp)
   width = Dimension.ratio("4:1")    // The width will be 40dp
// ---------
   width = Dimension.wrapContent
   height = Dimension.ratio("1:0.25")   // The height will be a fourth of the resulting wrapContent width

Visibility

As with the View system, you can set the visibility of a Composable.

  • Visibility.Visible - The default Visibility of Composables in ConstraintLayout
  • Visibility.Invisible - Effectively the same as setting the alpha to 0.0f
  • Visibility.Gone - The dimensions of the Composable will collapse to 0, and constraints will still be considered

Composables constrained to another with visibility Gone will use goneMargin instead:

constrain(title) {
   visibility = Visibility.Gone
}
constrain(name) {
   start.linkTo(title.end, margin = 4.dp, goneMargin = 8.dp)  // The margin in the layout will be 8dp
}

Helpers

Chains

Create horizontal and vertical chains by passing the ConstrainedLayoutReferences of each Composable that should be in the chain. The order each reference is passed will be reflected in the chain.

   // text1 is the head (left-most element) of the chain
   createHorizontalChain(text1, text2, button1, ChainStyle.Packed)

Additionally, chains can be constrained on their axis to reposition them (by default, they are constrained to the parent). Do not try to re-define the constraints for the elements in the chain, it will result in unexpected behavior.

   // text1 start is constrained to parent.start, button1 end is constrained to parent.end
   val chain1 = createHorizontalChain(text1, button1)

   constrain(chain1) {
      // text1 start will now be constrained to vGuideline1
      start.linkTo(vGuideline1)

      // button1 end will now be constrained to vGuideline2
      end.linkTo(vGuideline2)
   }

   constraint(text1) {
      // DO NOT DO THIS if you wish to constrain the chain to a different element
      start.linkTo(vGuideline1)
      
      // You can however, change the constrains on the axis not managed by the chain
      top.linkTo(button1.bottom)
   }

Guidelines

Guidelines can be created with respect to an anchor of the parent (the ConstraintLayout Composable).

  • createGuidelineFromAbsoluteLeft
  • createGuidelineFromStart
  • createGuidelineFromAbsoluteRight
  • createGuidelineFromEnd
  • createGuidelineFromTop
  • createGuidelineFromBottom

And can be defined as a distance from the anchor in Dp, or as a percentage of the total dimension on the respective axis.

   // located at 10dp from the end anchor
   val g1 = createGuidelineFromEnd(10.dp)

   // located at 30% the distance of the total width from the end anchor
   // Eg: for a ConstraintLayout with width of 100dp, the guideline will be at 30dp from the end
   val g2 = createGuidelineFromEnd(0.3f)

Barriers

Use barriers to be able to constrain to the outermost anchor within a group of elements.

   // The barrier will act as an anchorable point that is always situated
   // at the end-most point of text1 and text2 with an additional 2dp margin
   val barrier1 = createEndBarrier(text1, text2, margin = 2.dp)

   constrain(button1) {
      // The barrier guarantees the button never overlaps with text1 or text2
      start.linkTo(barrier1)
   }

A barrier can be created for each anchor:

  • createAbsoluteLeftBarrier
  • createStartBarrier
  • createAbsoluteRightBarrier
  • createEndBarrier
  • createTopBarrier
  • createBottomBarrier