How to highlight specific word of the text in jetpack compose?
Asked Answered
H

4

12

I wanted to know how to highlight the specific part of the text in jetpack compose. I tried Html.fromHtml() like this

Text(text = Html.fromHtml(" <font color='red'> Hello </font> World").toString())

But it didn't work. Is there any way I can do this in compose?

Hemlock answered 3/4, 2021 at 14:20 Comment(0)
B
22

You can use the AnnotatedString to display the text with multiple styles.

Something like:

Text(buildAnnotatedString {
    withStyle(style = SpanStyle(color = Color.Red)) {
        append("Hello")
    }
    append(" World ")
})

enter image description here

Benares answered 3/4, 2021 at 14:28 Comment(2)
What if I already have a string and I want to highlight from a specific index to another specific index?Lannielanning
@BugsHappen Check this https://mcmap.net/q/891723/-how-to-highlight-specific-word-of-the-text-in-jetpack-composeSimilarity
E
9

You can use AnnotatedString to append each word/section with it's own style or to add different style at any index which is great if you're using a string resource.

For the hello world example you could construct something like this:


val annotatedString = buildAnnotatedString {
    val str = "Hello World" // or stringResource(id = R.string.hello_world)
    val boldStr = "Hello" // or stringResource(id = R.string.hello)
    val startIndex = str.indexOf(boldStr)
    val endIndex = startIndex + boldStr.length
    append(str)
    addStyle(style = SpanStyle(color = Color.Red), start = startIndex, end = endIndex)
}
Text(
    text = annotatedString,
)

Hello world text with the letters of hello in red

Using addStyle in this way allows us to do some fun things like adding multiple styles to the same text

val annotatedString = buildAnnotatedString {
    val str = "Hello Wonderful World" // or stringResource(id = R.string.hello_world)
    val boldStr = "Wonderful World" // or stringResource(id = R.string.world)
    val startIndex = str.indexOf(boldStr)
    val endIndex = startIndex + boldStr.length
    append(str)
    addStyle(style = SpanStyle(color = Color.Red), start = startIndex, end = endIndex)

    val italicsStr = "Wonderful"
    val italicsStartIndex = str.indexOf(italicsStr)
    val italicsEndIndex = startIndex + italicsStr.length
    addStyle(style = SpanStyle(fontStyle = FontStyle.Italic), start = italicsStartIndex, end = italicsEndIndex)
}
Text(
    text = annotatedString,
    style = TextStyle(fontWeight = FontWeight.Bold),
    color = Color.Blue,
)

The text hello wonderful world with varied color and italics

Ellord answered 15/11, 2022 at 23:4 Comment(1)
Thanks for mentioning addStyle() - I was already down a rabbit hole of splitting and reassembling the string. When highlighting auto-complete options with this method, it's a good idea to also check if (startIndex > -1) before calling it.Ulland
S
4

Check this function below. Here paragraph is your string source and searchQuery is the specific text you want to highlight.

This provides you a dynamic state for text and search highlights.

@Composable
    fun getData(): StateFlow<AnnotatedString?> {

        val span = SpanStyle(
            color = MaterialTheme.colorScheme.onPrimaryContainer,
            fontWeight = FontWeight.SemiBold,
            background = MaterialTheme.colorScheme.primaryContainer
        )

        return combine(paragraph, searchQuery) { text, query ->
            buildAnnotatedString {
                var start = 0
                while (text.indexOf(query, start, ignoreCase = true) != -1 && query.isNotBlank()) {
                    val firstIndex = text.indexOf(query, start, true)
                    val end = firstIndex + query.length
                    append(text.substring(start, firstIndex))
                    withStyle(style = span) {
                        append(text.substring(firstIndex, end))
                    }
                    start = end
                }
                append(text.substring(start, text.length))
                toAnnotatedString()
            }
        }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
    }
Similarity answered 24/9, 2022 at 9:10 Comment(0)
A
3

Inspired from Matt Smith's answer but in a more reusable and flexible way with taking a list of Pair precising the placeholder and a SpanStyle (instead of a Pair it can be a custom data class if you need anything else...)

Then iterate over the list to annotate the string with the corresponding SpanStyle to their placeholder.

@Composable
fun annotateRecursively(
    placeHolderList: List<Pair<String, SpanStyle>>,
    originalText: String
): AnnotatedString {
    var annotatedString = buildAnnotatedString { append(originalText) }
    for (item in placeHolderList) {
        annotatedString = buildAnnotatedString {
            val startIndex = annotatedString.indexOf(item.first)
            val endIndex = startIndex + item.first.length
            append(annotatedString)
            addStyle(style = item.second, start = startIndex, end = endIndex)
        }
    }
    return annotatedString
}
Amaryllidaceous answered 23/3, 2023 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.