Text Styling in Compose — AnnotatedString, SpanStyle & Rich Text
Create rich text with inline styling, clickable regions, and advanced text control in Jetpack Compose.
Typography and Basic Styling
@Composable
fun TypographyExample() {
Column(modifier = Modifier.padding(16.dp)) {
Text(
"Headline",
style = MaterialTheme.typography.headlineSmall
)
Text(
"Body text with default styling",
style = MaterialTheme.typography.bodyMedium
)
Text(
"Caption",
style = MaterialTheme.typography.labelSmall
)
}
}
AnnotatedString with SpanStyle
@Composable
fun StyledTextExample() {
val annotated = buildAnnotatedString {
append("This is ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append("bold text")
}
append(" and this is ")
withStyle(style = SpanStyle(color = Color.Red)) {
append("red text")
}
append(". Here's some ")
withStyle(
style = SpanStyle(
background = Color.Yellow,
color = Color.Black
)
) {
append("highlighted")
}
append(" text.")
}
Text(annotated)
}
ClickableText with URL Annotations
@Composable
fun ClickableTextExample() {
val annotated = buildAnnotatedString {
append("Visit our ")
pushStringAnnotation(
tag = "URL",
annotation = "https://example.com"
)
withStyle(style = SpanStyle(color = Color.Blue, textDecoration = TextDecoration.Underline)) {
append("website")
}
pop()
append(" for more info.")
}
ClickableText(
text = annotated,
onClick = { offset ->
annotated.getStringAnnotations(
tag = "URL",
start = offset,
end = offset
).firstOrNull()?.let { annotation ->
println("Clicked: ${annotation.item}")
// Open browser
}
}
)
}
ParagraphStyle for Layout Control
@Composable
fun ParagraphStyleExample() {
val annotated = buildAnnotatedString {
withStyle(
style = ParagraphStyle(
textAlign = TextAlign.Center,
lineHeight = 20.sp
)
) {
append("Centered paragraph\nwith custom line height")
}
append("\n\n")
withStyle(
style = ParagraphStyle(
textAlign = TextAlign.Justify,
textIndent = TextIndent(firstLine = 8.sp)
)
) {
append("Indented justified text starts here with 8sp indent.")
}
}
Text(annotated, modifier = Modifier.padding(16.dp))
}
SelectionContainer and DisableSelection
@Composable
fun SelectableTextExample() {
Column(modifier = Modifier.padding(16.dp)) {
SelectionContainer {
Column {
Text("This text is selectable")
DisableSelection {
Text("This text is NOT selectable")
}
Text("This is selectable again")
}
}
}
}
Text Overflow with Expand/Collapse
@Composable
fun ExpandableTextExample() {
var isExpanded by remember { mutableStateOf(false) }
val displayText = if (isExpanded) {
"This is the full text with all details. " +
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
} else {
"This is the full text with all details..."
}
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = displayText,
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth()
)
TextButton(onClick = { isExpanded = !isExpanded }) {
Text(if (isExpanded) "Show Less" else "Show More")
}
}
}
Complex Rich Text Example
@Composable
fun RichTextArticle() {
val content = buildAnnotatedString {
withStyle(style = SpanStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold)) {
append("Article Title")
}
append("\n\n")
withStyle(style = SpanStyle(color = Color.Gray, fontSize = 12.sp)) {
append("By Author • March 2, 2025")
}
append("\n\n")
withStyle(style = ParagraphStyle(lineHeight = 1.5.em)) {
append("This is the opening paragraph with ")
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic)) {
append("italic emphasis")
}
append(" and a ")
pushStringAnnotation("LINK", "https://example.com")
withStyle(style = SpanStyle(color = Color.Blue, textDecoration = TextDecoration.Underline)) {
append("clickable link")
}
pop()
append(".")
}
}
SelectionContainer {
Text(
text = content,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
}
}
Key Takeaways
- Use AnnotatedString for mixed styling within single Text
- Apply SpanStyle for character-level formatting (bold, color, background)
- Use ParagraphStyle for block-level control (alignment, indentation)
- ClickableText enables interactive regions with annotations
- SelectionContainer makes text selectable; DisableSelection for specific regions
- Implement expand/collapse with maxLines and TextOverflow.Ellipsis
8 Android app templates: Gumroad
Top comments (0)