Annyce Davis

Davis Technology Consulting

  • Home
  • About Me
  • Blog
  • Courses
  • Newsletter

Programmatically laying out views in ConstraintLayout with Flow

March 17, 2021 by Annyce Davis

Rotating the phone to landscape caused users of the app to experience a suboptimal experience. Turns out we had some legacy code that was manually calculating the size and position of each “chip”. 😱

Buggy Profile screen in landscape

The problem

How can we transform the following code into something that is simpler to reason on and maintain?

last == null -> {
    addView(textView, firstPillLayoutParams())
    currentLineWidth = thisWidth
    below = textView

    currentDepth += topMarginFor(textView) + textViewHeight
}

currentLineWidth + thisWidth <= actualWidth -> {
    addView(textView, sameLineLayoutParams(last))
    currentLineWidth += thisWidth
}

Enter ConstraintLayout with Flow.

The solution

In ConstraintLayout 2.0, the Flow virtual layout was introduced. If you’re unfamiliar with Flow, this blog post provides a simple introduction. The main benefits of using Flow are:

  • it supports long chains of items that can wrap to multiple rows
  • it allows you to keep your layout hierarchy flat
  • elements can be positioned vertically or horizontally

Bingo. That’s exactly what I needed.

Creating the Flow instance

We’ll start by creating the Flow instance and adding it to the ConstraintLayout. We want it to have a unique id that we can use for layout purposes. Additionally, the flow should be the same size as the parent’s width and then the height should match its contents.

val flow = Flow(context).apply {
    id = generateViewId()
    
    layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
}
addView(flow)

At this point, each element is displayed in a row. Some of the “chips” are clipped and there’s no spacing between them. Let’s add some styling to get us closer to the desired experience.

Flow with no styling

We’ll set the wrapMode to Flow.WRAP_CHAIN. That will allows the items to wrap to additional rows. We’ll also set a few more parameters to establish the alignment of the chips in the rows. This gets us event closer to what we want.

setWrapMode(Flow.WRAP_CHAIN)
setHorizontalStyle(Flow.CHAIN_PACKED)
setHorizontalAlign(Flow.HORIZONTAL_ALIGN_START)
Flow with minor styling

Next, we set the horizontalBias to 0f so that the “chips” will start towards the left for each row instead of in the center. Then we put in gaps for both the vertical and horizontal directions.

setHorizontalBias(0f)
setVerticalGap(pillMarginWidth)
setHorizontalGap(pillMarginWidth)
setOrientation(Flow.HORIZONTAL)

Now we have just we need. A flexible layout that’s easy to reason on and looks nice in both portrait and landscape modes. šŸ‘Œ

Profile screen with use of ConstraintLayout and Flow

The complete function

Thanks for reading!

Share this:

  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on Bluesky (Opens in new window) Bluesky
  • Click to share on WhatsApp (Opens in new window) WhatsApp
  • Click to share on Reddit (Opens in new window) Reddit

Related

Filed Under: Android, Kotlin Tagged With: Android, ConstraintLayout, Flow, Kotlin

Follow Me

  • Bluesky

Categories

  • Android (60)
  • Career (5)
  • Communication (4)
  • Flutter (1)
  • Git (4)
  • Gradle (4)
  • Grails (23)
  • iOS (1)
  • Java (8)
  • JavaScript (6)
  • Kotlin (17)
  • Life (5)
  • Public Speaking (26)
  • Revenue (2)
  • RxJava (1)
  • Software Development (13)
  • Twitter (3)
  • Uncategorized (11)
  • Video Course (5)

Follow Me

  • Bluesky

Copyright © 2025 Ā· All Rights Reserved Ā· Log in

 

Loading Comments...