How to implement Expandable Text in Jetpack Compose
The Expandable Text component provides access to truncated text with a … Show More/Show Less button that toggles the visibility of the full text. This feature allows you to seamlessly expand or collapse text within the content, providing flexibility to adapt to different use cases.
Use ExpandableText if:
- You need to handle long texts or descriptions.
Do not use ExpandableText if:
- You can provide short and meaningful alternatives.
- The content is critical for the user.
First add the following dependency in build.gradle(Module: App) if you don’t have them.
dependencies {
implementation 'androidx.compose.material:material:1.5.4'
}
//This if you are using .kts
//implementation("androidx.compose.material:material:1.5.4")
To implement this create the ExpandableText class inside the components folder in your project and copy the following code.
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.TextUnit
const val DEFAULT_MINIMUM_TEXT_LINE = 3
/**
* An expandable text component that provides access to truncated text with a dynamic ... Show More/ Show Less button.
*
* @param modifier Modifier for the Box containing the text.
* @param textModifier Modifier for the Text composable.
* @param style The TextStyle to apply to the text.
* @param fontStyle The FontStyle to apply to the text.
* @param text The text to be displayed.
* @param collapsedMaxLine The maximum number of lines to display when collapsed.
* @param showMoreText The text to display for "... Show More" button.
* @param showMoreStyle The SpanStyle for "... Show More" button.
* @param showLessText The text to display for "Show Less" button.
* @param showLessStyle The SpanStyle for "Show Less" button.
* @param textAlign The alignment of the text.
* @param fontSize The font size of the text.
*/
@Composable
fun ExpandableText(
modifier: Modifier = Modifier,
textModifier: Modifier = Modifier,
style: TextStyle = LocalTextStyle.current,
fontStyle: FontStyle? = null,
text: String,
collapsedMaxLine: Int = DEFAULT_MINIMUM_TEXT_LINE,
showMoreText: String = "... Show More",
showMoreStyle: SpanStyle = SpanStyle(fontWeight = FontWeight.W500),
showLessText: String = " Show Less",
showLessStyle: SpanStyle = showMoreStyle,
textAlign: TextAlign? = null,
fontSize: TextUnit
) {
// State variables to track the expanded state, clickable state, and last character index.
var isExpanded by remember { mutableStateOf(false) }
var clickable by remember { mutableStateOf(false) }
var lastCharIndex by remember { mutableStateOf(0) }
// Box composable containing the Text composable.
Box(modifier = Modifier
.clickable(clickable) {
isExpanded = !isExpanded
}
.then(modifier)
) {
// Text composable with buildAnnotatedString to handle "Show More" and "Show Less" buttons.
Text(
modifier = textModifier
.fillMaxWidth()
.animateContentSize(),
text = buildAnnotatedString {
if (clickable) {
if (isExpanded) {
// Display the full text and "Show Less" button when expanded.
append(text)
withStyle(style = showLessStyle) { append(showLessText) }
} else {
// Display truncated text and "Show More" button when collapsed.
val adjustText = text.substring(startIndex = 0, endIndex = lastCharIndex)
.dropLast(showMoreText.length)
.dropLastWhile { Character.isWhitespace(it) || it == '.' }
append(adjustText)
withStyle(style = showMoreStyle) { append(showMoreText) }
}
} else {
// Display the full text when not clickable.
append(text)
}
},
// Set max lines based on the expanded state.
maxLines = if (isExpanded) Int.MAX_VALUE else collapsedMaxLine,
fontStyle = fontStyle,
// Callback to determine visual overflow and enable click ability.
onTextLayout = { textLayoutResult ->
if (!isExpanded && textLayoutResult.hasVisualOverflow) {
clickable = true
lastCharIndex = textLayoutResult.getLineEnd(collapsedMaxLine - 1)
}
},
style = style,
textAlign = textAlign,
fontSize = fontSize
)
}
}
By default, this component is configured to display a minimum of 3 lines, as specified by the collapsedMaxLine
parameter, ensuring that the "Show More" button appears. However, you can customize this behavior by adjusting the collapsedMaxLine
parameter, as well as modifying the text for "Show More" and "Show Less" using the showMoreText
and showLessText
parameters, respectively.
To use it you just have to call the component in the place where you need it with the following code as an example.
//val dimen_3dp = 3.dp
//val dimen_5dp = 5.dp
//<string name="about_label">About</string>
@Composable
fun About(aboutInfo: String = "") {
Column(
modifier = Modifier.padding(dimen_5dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.Start
) {
Text(
color = MaterialTheme.colors.primary,
fontSize = 20.sp,
style = MaterialTheme.typography.h4,
text = stringResource(id = R.string.about_label)
)
ExpandableText(
fontSize = 12.sp,
text = aboutInfo,
modifier = Modifier.padding(dimen_3dp)
)
}
}
@Preview
@Composable
fun PreviewAbout() {
About()
}
In this scenario, you can enhance the component by incorporating text size and padding adjustments. These parameters offer additional customization options, allowing you to tailor the appearance of the text to suit your preferences.
Note. you can expand or collapse the text by pressing anywhere on the component.
Run your project and at the end you will see something similar to this:
I hope this tutorial works for you and you like it, Below you will find the url of the repository. See you!