Skip to content

Instantly share code, notes, and snippets.

@ShellWen
Created March 13, 2023 18:26
Show Gist options
  • Select an option

  • Save ShellWen/85e07cb65fc07a7d81802ea41a463ae0 to your computer and use it in GitHub Desktop.

Select an option

Save ShellWen/85e07cb65fc07a7d81802ea41a463ae0 to your computer and use it in GitHub Desktop.
SimpleLinkText -- A simple wrapper for ClickableText.
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.text.*
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
class SimpleLinkTextHolder(private val uriHandler: UriHandler) {
sealed class TextType {
data class Text(val text: String) : TextType()
data class Link(val text: String, val onClick: (link: TextType.Link) -> Unit) : TextType() {
val tag = onClick.hashCode().toString()
}
}
val textList = mutableListOf<TextType>()
fun text(text: String) {
textList.add(TextType.Text(text))
}
fun link(text: String, link: String) {
textList.add(TextType.Link(text) {
uriHandler.openUri(link)
})
}
fun link(text: String, onClick: (link: TextType.Link) -> Unit) {
textList.add(TextType.Link(text, onClick))
}
fun toAnnotatedString(): AnnotatedString {
return buildAnnotatedString {
var currentIndex = 0
textList.forEach { text ->
when (text) {
is TextType.Text -> {
append(text.text)
currentIndex += text.text.length
}
is TextType.Link -> {
val tag = text.tag
addStringAnnotation(
tag = tag,
annotation = text.text,
start = currentIndex,
end = currentIndex + text.text.length
)
addStyle(
style = SpanStyle(textDecoration = TextDecoration.Underline),
start = currentIndex,
end = currentIndex + text.text.length
)
append(text.text)
currentIndex += text.text.length
}
}
}
}
}
}
@Composable
fun SimpleLinkText(
modifier: Modifier = Modifier,
style: TextStyle = TextStyle(
color = MaterialTheme.colorScheme.primary,
),
softWrap: Boolean = true,
overflow: TextOverflow = TextOverflow.Clip,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
textBuilder: SimpleLinkTextHolder.() -> Unit
) {
val uriHandler = LocalUriHandler.current
val simpleLinkTextHolder = remember {
SimpleLinkTextHolder(uriHandler).apply(textBuilder)
}
val annotatedLinkString = remember {
simpleLinkTextHolder.toAnnotatedString()
}
ClickableText(
modifier = modifier,
text = annotatedLinkString,
onClick = { offset ->
val tagWithTexts =
simpleLinkTextHolder.textList.filterIsInstance<SimpleLinkTextHolder.TextType.Link>()
.associateBy { it.tag }
tagWithTexts.forEach {
annotatedLinkString.getStringAnnotations(it.key, offset, offset).firstOrNull()?.let { _ ->
it.value.onClick.invoke(it.value)
}
}
},
style = style,
softWrap = softWrap,
overflow = overflow,
maxLines = maxLines,
onTextLayout = onTextLayout
)
}
val uriHandler = LocalUriHandler.current
SimpleLinkText(modifier = Modifier.padding(vertical = 16.dp, horizontal = 32.dp)) {
text("You can click ")
link("GitHub", "https://github.com")
text(" to open GitHub in your browser. ")
text("You can click ")
link("Google") {
uriHandler.openUri("https://google.com")
}
text(" to open Google in your browser.")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment