When designing user interfaces, it’s essential to balance functionality with visual clarity. In Jetpack Compose, creating layouts that adapt dynamically to content size and available space is easy. In this article, we’ll explore how to create an expandable FlowRow, a versatile layout component that adapts to screen size and prevents visual clutter. Imagine a settings screen with dozens of options or a tag list with numerous categories. An expandable FlowRow can elegantly present these elements, allowing users to easily access all information without overwhelming them.
What is a FlowRow?
FlowRow is a layout part of the Jetpack Compose Foundation library. It arranges child elements horizontally, and when there’s no more space available in the current row, it wraps the remaining items to the next line. Unlike traditional row layouts, FlowRow manages dynamic content by automatically adjusting to fit its contents. There’s no need to handle line breaks or spacing manually. Whether you’re dealing with a list of user-generated content, categories, or a collection of small components, FlowRow ensures the layout is clean and responsive, adapting to the available screen width. This makes FlowRow ideal for displaying multiple items, like chips, tags, or buttons, that need to flow across multiple lines in a responsive layout.
For example, if the width of the screen can’t fit all the items on a single row, FlowRow will wrap the remaining items to the next row, avoiding horizontal scrolling and keeping the content visible.
FlowRow(
modifier = Modifier.padding(4.dp),
horizontalArrangement = Arrangement.Start
) {
repeat(12) {
SuggestionChip(
modifier = Modifier.padding(4.dp),
label = { Text("Item") }, onClick = { })
}
}
In this example, the FlowRow display the items on a single row if there’s enough space. When space runs out, it will push the remaining items to the next line.
Similarly, Compose offers FlowColumn, which applies the same principles vertically. In FlowColumn, child components are arranged in a column, and when there’s no more vertical space available, the remaining items wrap to the next column. This is useful for layouts where you want items to stack vertically and then wrap when they exceed the available space.
Both FlowRow and FlowColumn provide flexible, flowing layouts that adapt based on the size of their content and the available space.
Building the Expandable FlowRow
Creating an expandable FlowRow involves designing a dynamic layout where a limited number of items are displayed initially, and additional items can be revealed upon user interaction. To achieve this, we’ll use Jetpack Compose’s ContextualFlowRow and introduce an expansion mechanism with an indicator that shows how many items remain hidden.
ContextualFlowRow is an enhanced version of FlowRow that introduces more control over how items are displayed, specifically handling overflow in a dynamic way. It allows you to set a maximum number of visible lines or items and provides a mechanism for displaying indicators (such as expand/collapse buttons) when not all items are shown. This composable is useful when you need to display a large number of items, but want to avoid overwhelming the user by showing too many at once.
Let’s break down the code and implementation step by step.
First, we need to control how many lines of items are visible at any given time. For this, we introduce a mutable state variable, maxLines, which will determine the number of lines shown before truncating the row.
var maxLines by remember {
mutableIntStateOf(2)
}
Next, we design the composable moreOrCollapseIndicator, which serves as the UI element allowing the user to expand or collapse the ContextualFlowRow. This indicator shows the number of hidden items and toggles between expanding and collapsing the row when clicked.
val moreOrCollapseIndicator = @Composable { scope: ContextualFlowRowOverflowScope ->
val remainingItems = elements.size - scope.shownItemCount
SuggestionChip(
label = { Text(text = if (remainingItems == 0) "--" else "+$remainingItems") },
onClick = {
if (remainingItems == 0) {
maxLines = 2
} else {
maxLines += 5
}
}
)
}
Job Offers
Really important here is the scope.shownItemCount, it provides the count of currently visible items. We use this to calculate remainingItems, which gives us the number of items hidden from view.
Now, we incorporate this indicator into the ContextualFlowRow, which will display our list of items and handle overflow dynamically.
ContextualFlowRow(
modifier = Modifier
.fillMaxWidth()
.animateContentSize(),
verticalArrangement = Arrangement.spacedBy(4.dp),
horizontalArrangement = Arrangement.Center,
maxLines = maxLines,
overflow = ContextualFlowRowOverflow.expandOrCollapseIndicator(
expandIndicator = moreOrCollapseIndicator,
collapseIndicator = moreOrCollapseIndicator
),
itemCount = elements.size
) { index ->
SuggestionChip(
modifier = Modifier.padding(horizontal = 12.dp),
label = { Text(text = elements[index].first) },
onClick = {},
icon = elements[index].second
)
}
The maxLines limits how many lines of items are displayed. When the number of items exceeds this limit, the expandOrCollapseIndicator shows up, allowing the user to expand or collapse the row.
The result will be something like this:
Conclusion
Creating an expandable FlowRow with an indicator provides a clean, user-friendly way to display many items without cluttering the screen. With just a few lines of code, you can implement a dynamic layout that adapts to user interaction and device screen size. The ability to expand and collapse items in a responsive flow layout improves the user experience, especially when dealing with lists of items that may exceed the available screen space.
By combining the flexibility of FlowRow, dynamic state management, and contextual indicators, we can create highly interactive and user-friendly UIs in Jetpack Compose.
Feel free to share your comments, or if you prefer, you can reach out to me on LinkedIn.
Have a great day!
This article is previously published on proandoiddev.com