Sometimes, we have limited space to display large amounts of text. One solution is to restrict the number of displayed lines and ellipsize the end of the last line. However, we still need to display the full content. We can use various implementations of expanded text, but here I demonstrate how to add a "Show more" button and display the full text in a BottomSheet.
First, we need to create an appropriate container to display the text with a fixed height. The simplest implementation of it looks like this:
@Composable
fun ContentBlock() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(160.dp)
.background(color = Color.Yellow, shape = RoundedCornerShape(8.dp))
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(8.dp),
verticalArrangement = Arrangement.Bottom
) {
Text(
text = mockedText,
overflow = TextOverflow.Ellipsis
)
}
}
}
It is a very simple container for text. When the text fills all available space, it is cut off in a way defined by the overflow
property. In our example, TextOverflow.Ellipsis
indicates that the last line will end with three dots.
We want to give users the ability to read the full text. For this reason, we need to add a "More" button at the bottom of the block. The simplest solution is to add a button labeled "More" directly below the message block:
Text(
text = "More",
modifier = Modifier
.align(Alignment.End)
)
However, this doesn't work because the added text pushes other elements outside the block's border, rendering it invisible. After trying different approaches, I found the simplest solution: adding weight for each element to ensure both elements are visible within the same column.
@Composable
fun ContentBlock() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(160.dp)
.background(color = Color.Yellow, shape = RoundedCornerShape(8.dp))
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(8.dp),
verticalArrangement = Arrangement.Bottom
) {
Text(
text = mockedText,
modifier = Modifier
.weight(0.85f),
overflow = TextOverflow.Ellipsis
)
Text(
text = "More",
modifier = Modifier
.weight(0.15f)
.align(Alignment.End)
)
}
}
}
We successfully displayed the "More" button. Now, we need to implement the logic to display this button only when necessary, as displaying it when the text fully fits would be redundant. Thanks for the Text
composable in Jetpack Compose, which provides the necessary API for this functionality. We can just set up a callback for onTextLayout
property, which receives aTextLayoutResult
object. It contains detailed information about the text layout state, but in our case, we use the hasVisualOverflow
flag, which indicates whether the text exceeds its boundaries. Based on this flag, we decided whether or not to display the "More" button.
Text(
text = mockedText,
modifier = Modifier
.weight(0.85f),
overflow = TextOverflow.Ellipsis,
onTextLayout = { result ->
isShowMore.value = result.hasVisualOverflow
}
)
if(isShowMore.value) {
Text(
text = "More",
modifier = Modifier
.weight(0.15f)
.align(Alignment.End)
)
}
This solution seems unnecessary for a single text block that can be resized based on its content. However, it is applicable when using a horizontal pager, and we want to prevent size jumps during swiping. We only need to implement logic to display the full content of the text block when the "More" button is pressed. One simple approach is to use a bottom sheet, but I won't cover that in this post.
You can find the full sample for this composable element in this GitHub Gist. I welcome any thoughts or comments on this post.
You can find more useful content on my LinkedIn page, on X, in Medium or Mastodon.
Top comments (0)