I'm struggling with vertically centering text in Jetpack Compose version alpha-11. It appears that my font has a significant amount of padding and I'm unable to find a way to disable it. This has come up only once before on SO, as far as I can tell, here, but their answer of using a constraint layout seems to suggest that they simply positioned it absolutely, which isn't exactly a solution as much as a workaround, and something I'd like to avoid.
You can see it clearly in the screenshot below.
The code for that looks like this:
Column(verticalArrangement = Arrangement.Center) {
Text(
text = "Let's Go",
color = Color.White,
fontSize = 120.sp,
fontFamily = oswaldLightFontFamily(),
textAlign = TextAlign.Center,
modifier = Modifier.background(Color.Blue)
)
}
The arguments you would expect to position it -- verticalArrangement and textAlign -- do not do anything here but I'm including them to demonstrate what I've tried.
My workaround so far has been to use Modifier.graphicsLayer(translationY = -25f) to shift it up but that seems like a terrible hack for something that should be so straightforward. It appears that in classic Android layouts, one could set android:includeFontPadding="false" and that would bypass this behavior but there doesn't seem to be a similar option in Jetpack Compose.
Anyone encounter this?
According to https://issuetracker.google.com/issues/171394808, It seems this is one of the limitations of the current JetPack Compose.
This is also deal breaker for my app because the font used rely heavily with the includeFontPadding. For current workaround, I create a CoreText that wraps TextView inside my compose.
Here's example of my wrapper, its not perfect but it does the job for my current use case:
#Composable
fun CoreText(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
overflow: TextOverflow = TextOverflow.Clip,
maxLines: Int = Int.MAX_VALUE,
style: TextStyle = Typography.body2,
onClick: (() -> Unit)? = null,
) {
AndroidView(
modifier = modifier,
factory = { context ->
TextView(context)
},
update = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
it.setTextAppearance(style.fontWeight.toStyle())
} else {
it.setTextAppearance(it.context, style.fontWeight.toStyle())
}
if (overflow == TextOverflow.Ellipsis) {
it.ellipsize = TextUtils.TruncateAt.END
}
if (textDecoration != null) {
it.paintFlags = when (textDecoration) {
TextDecoration.Underline -> {
Paint.UNDERLINE_TEXT_FLAG
}
TextDecoration.LineThrough -> {
Paint.STRIKE_THRU_TEXT_FLAG
}
else -> 0
}
}
if (onClick != null) {
it.setOnClickListener { onClick.invoke() }
}
if (color != Color.Unspecified || style.color != Color.Unspecified) {
it.setTextColor(if (color == Color.Unspecified) style.color.toArgb() else color.toArgb())
}
it.textSize = style.fontSize.value
it.text = text
it.background = ColorDrawable(style.background.toArgb())
it.maxLines = maxLines
it.includeFontPadding = false
it.textAlignment = textAlign?.toStyle() ?: style.textAlign.toStyle()
}
)
}
// Replace with your style
fun FontWeight?.toStyle(): Int {
return when (this) {
FontWeight.Bold -> R.style.TextStyle_Bold
FontWeight.Normal -> R.style.TextStyle_Regular
FontWeight.Medium, FontWeight.SemiBold -> R.style.TextStyle_Medium
else -> -1
}
}
fun TextAlign?.toStyle(): Int {
return when (this) {
TextAlign.Left -> TEXT_ALIGNMENT_TEXT_START
TextAlign.Right -> TEXT_ALIGNMENT_TEXT_END
TextAlign.Center -> TEXT_ALIGNMENT_CENTER
TextAlign.Start -> TEXT_ALIGNMENT_TEXT_START
TextAlign.End -> TEXT_ALIGNMENT_TEXT_END
else -> -1
}
}
This happens due to uneven font padding on https://fonts.google.com/specimen/Oswald, plus the text you're using in lowercase makes the discrepancy more obvious.
As #Siyamed mentioned below, the API to turn the default includeFontPadding behaviour off in Compose was released with Compose 1.2 beta and you use it like so:
Text(
...
textStyle = TextStyle(
platformStyle = PlatformTextStyle(
includeFontPadding = false
),
)
https://android-developers.googleblog.com/2022/05/whats-new-in-jetpack-compose.html
give it a try, might help? Btw, the fact that PlatformTextStyle is "deprecated" only wants to inform that this is a compatibility API.
Compose now have TextStyle.platformStyle.incudeFontPadding that is set to true by default for version 1.2. you can set it to false in your TextStyle
Making the default false is something that Compose wants to do in v1.3 or later.
Just got around this same issue.
Box(contentAlignment = Alignment.Center){
Text(
text = "OK"
textAlign = TextAlign.Center
)
}
(Temporary) custom solution:
fun Modifier.baselinePadding(
firstBaselineToTop: Dp,
lastBaselineToBottom: Dp
) = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
val firstBaseline = placeable[FirstBaseline]
check(placeable[LastBaseline] != AlignmentLine.Unspecified)
val lastBaseline = placeable[LastBaseline]
val lastBaselineToBottomHeight = placeable.height - lastBaseline
val lastBaselineToBottomDelta = lastBaselineToBottom.roundToPx() - lastBaselineToBottomHeight
val totalHeight = placeable.height +
(firstBaselineToTop.roundToPx() - firstBaseline)
val placeableY = totalHeight - placeable.height
layout(placeable.width, totalHeight + lastBaselineToBottomDelta) {
placeable.placeRelative(0, placeableY)
}
}
By using Compose 1.2.0-alpha07 and above, you can use PlatformTextStyle api to set includeFontPadding.
Try to the below code:
private val NotoSans = FontFamily(
Font(R.font.noto_san_jp_black, FontWeight.Black),
Font(R.font.noto_san_jp_light, FontWeight.Light),
Font(R.font.noto_san_jp_bold, FontWeight.Bold),
Font(R.font.noto_san_jp_thin, FontWeight.Thin),
Font(R.font.noto_san_jp_medium, FontWeight.Medium),
Font(R.font.noto_san_jp_regular, FontWeight.Normal),
)
val Typography = Typography(
headlineLarge = Typography().headlineLarge.copy(
fontFamily = NotoSans,
)
)
#OptIn(ExperimentalTextApi::class)
/* ... */
Text(
text = "地域のお得は\nすべてここに",
style = MaterialTheme.typography.headlineLarge.copy(
platformStyle = PlatformTextStyle(
includeFontPadding = false
)
/* ... */
)
)
The result when includeFontPadding = false:
The result when includeFontPadding = true or no using it:
More information:
Fixing Font Padding in Compose Text - Medium
Related
In LazyColumn when we use LazyListScope.items with Surface. Inside multiple items there is extra padding on TOP and BOTTOM. I want to remove this padding. I am using Surface component of Material 3. BOM version is compose_bom = "2022.11.00".
Please don't suggest any alpha or beta version fix. If Material 3 stable api don't have solution, then please suggest normal Surface Material.
PreviewCreateListView
#Preview(showBackground = true)
#Composable
fun PreviewCreateListView() {
CreateListView()
}
CreateListView
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun CreateListView() {
val itemList = listOf(1, 2, 3)
LazyColumn(
contentPadding = PaddingValues(16.dp),
) {
items(itemList) { item ->
Surface(
onClick = { },
color = Color.Blue
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "$item",
)
}
}
}
}
Output
The M3 Surface with the onClick parameter has a minimum touch target size (48.dp) for accessibility. It will include extra space outside the component to ensure that they are accessible.
You can override this behaviour applying false to the LocalMinimumInteractiveComponentEnforcement. If it is set to false there will be no extra space.
Something like:
CompositionLocalProvider(
LocalMinimumInteractiveComponentEnforcement provides false) {
Surface(
onClick = { },
color = Color.Blue
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "$item",
)
}
}
Note: LocalMinimumInteractiveComponentEnforcement requires at least
M2 1.4.0-alpha04 and M3 1.1.0-alpha04. Before you can use LocalMinimumTouchTargetEnforcement in the same way.
The Surface variant that you use, with a onClick parameter, enforces a minimum height for accessibility purposes, see this at line 221
If you want to remove the space, use the variant without the onClick argument and use a Modifier.clickable instead
#Composable
fun CreateListView() {
val itemList = listOf(1, 2, 3)
LazyColumn(
contentPadding = PaddingValues(16.dp),
) {
items(itemList) { item ->
Surface(
modifier = Modifier.clickable { },
color = Color.Blue
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "$item",
)
}
}
}
}
I have a string that contains html, how can I display this in a Jetpack compose Text?
In a TextView I would use a Spanned and do something like:
TextView.setText(Html.fromHtml("<p>something", HtmlCompat.FROM_HTML_MODE_LEGACY)
How can I do this with Text from Jetpack compose?
Same answer as Yhondri, but using HtmlCompat if you are targeting api >24:
#Composable
fun Html(text: String) {
AndroidView(factory = { context ->
TextView(context).apply {
setText(HtmlCompat.fromHtml(text, HtmlCompat.FROM_HTML_MODE_LEGACY))
}
})
}
I have done it this way instead of using TextView in AndroidView and it seems to work quite well for me. The below composable also wraps up the text and expands when you click on it.
#Composable
fun ExpandingText(
description: String,
modifier: Modifier = Modifier,
textStyle: TextStyle = MaterialTheme.typography.body2,
expandable: Boolean = true,
collapsedMaxLines: Int = 3,
expandedMaxLines: Int = Int.MAX_VALUE,
) {
val text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(description, Html.FROM_HTML_MODE_LEGACY)
} else {
HtmlCompat.fromHtml(description, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
var canTextExpand by remember(text) { mutableStateOf(true) }
var expanded by remember { mutableStateOf(false) }
val interactionSource = remember { MutableInteractionSource() }
Text(
text = text.toString(),
style = textStyle,
overflow = TextOverflow.Ellipsis,
maxLines = if (expanded) expandedMaxLines else collapsedMaxLines,
modifier = Modifier
.clickable(
enabled = expandable && canTextExpand,
onClick = { expanded = !expanded },
indication = rememberRipple(bounded = true),
interactionSource = interactionSource,
)
.animateContentSize(animationSpec = spring())
.then(modifier),
onTextLayout = {
if (!expanded) {
canTextExpand = it.hasVisualOverflow
}
}
)
}
Unfortunately, Jetpack compose does NOT support HTML yet...
So, what you could do is:
Option 1: Create your own HTML parser
Jetpack compose supports basic styling such as Bold, color, font etc.. So what you can do is loop through the original HTML text and apply text style manually.
Option 2: Integrate the old TextView into your Jetpack compose.
Please read: Adopting Compose in your app
Thanks.
You can integrate the old TextView into your Jetpack compose like follows:
AndroidView(factory = { context ->
TextView(context).apply {
text = Html.fromHtml(your_html)
}
})
More info: https://foso.github.io/Jetpack-Compose-Playground/viewinterop/androidview/
you can use the code below:
#Composable
private fun TextHtml() {
Text(text = buildAnnotatedString {
withStyle(style = SpanStyle(color = Gray600)) {
append("normal text")
}
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold,color = Gray700)) {
append("bold text ")
}
})
}
use withStyle to apply the html tags and use append() inside it to add the string
Currently I get this:
But I want something like this:
But also the text from "50" and "min" should be aligned to the top.
My code:
Row(verticalAlignment = Alignment.Bottom) {
Text(
text = "18",
color = MaterialTheme.colors.primaryVariant,
fontSize = 60.sp,
modifier = Modifier
.weight(1f).height(62.dp),
textAlign = TextAlign.End,
)
Text(
text = "hrs",
modifier = Modifier.weight(1f).height(16.dp),
)
}
Row(verticalAlignment = Alignment.Top) {
Text(
text = "50",
color = MaterialTheme.colors.primaryVariant,
fontSize = 28.sp,
modifier = Modifier.weight(1f).height(30.dp).align(Alignment.Top),
textAlign = TextAlign.End,
)
Text("min", modifier = Modifier.weight(1f))
}
As you see in my code I solved that by using the height property currently. But it doesn't work for the align top and it feels a bit wrong. is there a better way to make it work?
You can use the AnnotatedString to display the text with multiple styles.
Something like:
Row() {
Text(
text = buildAnnotatedString {
withStyle(style = SpanStyle(
color = MaterialTheme.colors.primaryVariant,
fontSize = 60.sp)) {
append("18")
}
append(" hrs ")
})
}
For the second case you can apply a BaselineShift to the min text:
Row() {
Text(
text = buildAnnotatedString {
withStyle(style = SpanStyle(
color = MaterialTheme.colors.primaryVariant,
fontSize = 28.sp)) {
append("50")
}
withStyle(style = SpanStyle(
baselineShift = BaselineShift(+0.65f))) {
append(" min ")
}
})
}
You can use AlignmentLine.LastBaseLine to properly size and position you're Text. You can do something like this:
Modifier.layout { measurable, constraints ->
// Measure the composable
val placeable = measurable.measure(constraints)
// Check the composable has a LastBaseline
check(placeable[LastBaseline] != AlignmentLine.Unspecified)
val lastBaseline = placeable[LastBaseline]
val placeableY = placeable.height - lastBaseline
val height = placeable.height - placeableY
layout(placeable.width, height) {
placeable.placeRelative(0, -placeableY)
}
}
If you want to completely remove even the bottom FirstBaseLine just subtract it to the height and that should do it.
From version 1.2.0-alpha05, includeFontPadding is turned off as they announced: (https://developer.android.com/jetpack/androidx/releases/compose-ui#1.2.0-alpha05)
Text: includeFontPadding is now turned off by default. The clipping issues as a result of includeFontPadding=false is handled and no clipping should occur for tall scripts.
In case you haven't found the right solution. You can try this:
Text(text = buildAnnotatedString{
withStyle(style =
ParagraphStyle(
platformStyle = PlatformParagraphStyle(includeFontPadding = false),
lineHeightStyle = LineHeightStyle(
LineHeightStyle.Alignment.Bottom,
LineHeightStyle.Trim.Both
)
)
){
append("18")
}
})
For various reasons a Text should always have at least the height equal to x lines of text, no matter if it has less than x lines of text. The Text and BasicText Composables only have a maxLines parameter but no minLines
I have tried the following (x = 3):
Text(
modifier = Modifier.sizeIn(minHeight = with(LocalDensity.current) {
(42*3).sp.toDp()
}),
color = MaterialTheme.colors.onPrimary,
text = "Sample", textAlign = TextAlign.Center,
style = MaterialTheme.typography.h2 /* fontSize = 42 */,
lineHeight = 42.sp
)
The resulting height is less than if the text would contain 3 lines
Back in View World Android, we could simply use minLines=3, how can we achieve this in Jetpack Compose?
Your code is almost correct, just set lineHeight to fontSize*4/3:
var lineHeight = MaterialTheme.typography.h2.fontSize*4/3
Text(
modifier = Modifier.sizeIn(minHeight = with(LocalDensity.current) {
(lineHeight*3).toDp()
}),
color = MaterialTheme.colors.onPrimary,
text = "Sample", textAlign = TextAlign.Center,
style = MaterialTheme.typography.h2,
lineHeight = lineHeight
)
But you can do something similar without calculations using onTextLayout callback:
fun main() = Window {
var text by remember { mutableStateOf("Hello, World!") }
var lines by remember { mutableStateOf(0) }
MaterialTheme {
Button(onClick = {
text += "\nnew line"
}) {
Column {
Text(text,
maxLines = 5,
style = MaterialTheme.typography.h2,
onTextLayout = { res -> lines = res.lineCount })
for (i in lines..2) {
Text(" ", style = MaterialTheme.typography.h2)
}
}
}
}
}
While we are waiting for Google implements this feature you can use this workaround:
#Preview
#Composable
fun MinLinesPreview() {
lateinit var textLayoutResult: TextLayoutResult
val text = "Title\ntitle\nTITLE\nTitle"
// val text = "title\ntitle\ntitle\ntitle"
// val text = "title\ntitle"
// val text = "title"
Text(
modifier = Modifier.fillMaxWidth(),
text = text.addEmptyLines(3), // ensures string has at least N lines,
textAlign = TextAlign.Center,
maxLines = 4,
)
}
fun String.addEmptyLines(lines: Int) = this + "\n".repeat(lines)
Now your Text has the same height regardless string content:
This solution is much more easier than calculate Text's bottom offset based on line height in onTextLayout (spoiler: start, center and last line have different height)
If one additional recomposition of the Text is fine for you, you can also make use of the onTextLayout callback of Text as a workaround until there is official support for minimum lines from Google:
val minLineCount = 4
var text by remember { mutableStateOf(description) }
Text(
text = text,
maxLines = minLineCount, // optional, if you want the Text to always be exactly 4 lines long
overflow = TextOverflow.Ellipsis, // optional, if you want ellipsizing
textAlign = TextAlign.Center,
onTextLayout = { textLayoutResult ->
// the following causes a recomposition if there isn't enough text to occupy the minimum number of lines!
if ((textLayoutResult.lineCount) < minLineCount) {
// don't forget the space after the line break, otherwise empty lines won't get full height!
text = description + "\n ".repeat(minLineCount - textLayoutResult.lineCount)
}
},
modifier = Modifier.fillMaxWidth()
)
This will also properly work with ellipsizing and any kind of font padding, line height style etc. settings your heart desires.
A "fake" 4-line Text (with, say, 2 empty lines at the end) will have the same height like a "real" 4 line Text with 4 fully occupied lines of text. This oftentimes can be super important when e.g. laying out multiple wrap_content-height cards horizontally next to each other and the Text (in combination with maxLines) should determine the height of the cards, while all cards should have the same height (and it should work in regular and tall languages, like Burmese).
Please note, that this will not work in Android Studio's preview. My guess is, that Studio doesn't allow recompositions in the preview for performance reasons.
Below is a solution that I came up with that will set the height to a specific number of lines (you could adapt the modifier to make it minLines) It is inspired by code found from the compose SDK
// Inspiration: https://github.com/androidx/androidx/blob/6075c715aea671a616890dd7f0fc9a50d96e75b9/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/MaxLinesHeightModifier.kt#L38
fun Modifier.minLinesHeight(
minLines: Int,
textStyle: TextStyle
) = composed {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
val resolvedStyle = remember(textStyle, layoutDirection) {
resolveDefaults(textStyle, layoutDirection)
}
val resourceLoader = LocalFontLoader.current
val heightOfTextLines = remember(
density,
textStyle,
layoutDirection
) {
val lines = (EmptyTextReplacement + "\n").repeat(minLines - 1)
computeSizeForDefaultText(
style = resolvedStyle,
density = density,
text = lines,
maxLines = minLines,
resourceLoader
).height
}
val heightInDp: Dp = with(density) { heightOfTextLines.toDp() }
val heightToSet = heightInDp + OutlinedTextBoxDecoration
Modifier.height(heightToSet)
}
// Source: https://github.com/androidx/androidx/blob/6075c715aea671a616890dd7f0fc9a50d96e75b9/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt#L61
fun computeSizeForDefaultText(
style: TextStyle,
density: Density,
text: String = EmptyTextReplacement,
maxLines: Int = 1,
resourceLoader: Font.ResourceLoader
): IntSize {
val paragraph = Paragraph(
paragraphIntrinsics = ParagraphIntrinsics(
text = text,
style = style,
density = density,
resourceLoader = resourceLoader
),
maxLines = maxLines,
ellipsis = false,
width = Float.POSITIVE_INFINITY
)
return IntSize(paragraph.minIntrinsicWidth.ceilToIntPx(), paragraph.height.ceilToIntPx())
}
// Source: https://github.com/androidx/androidx/blob/6075c715aea671a616890dd7f0fc9a50d96e75b9/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt#L47
internal const val DefaultWidthCharCount = 10
internal val EmptyTextReplacement = "H".repeat(DefaultWidthCharCount)
// Needed because paragraph only calculates the height to display the text and not the entire height
// to display the decoration of the TextField Widget
internal val OutlinedTextBoxDecoration = 40.dp
// Source: https://github.com/androidx/androidx/blob/6075c715aea671a616890dd7f0fc9a50d96e75b9/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextDelegate.kt#L296
internal fun Float.ceilToIntPx(): Int = ceil(this).roundToInt()
Additional discussion on this implementation and other options can be found here:
https://kotlinlang.slack.com/archives/CJLTWPH7S/p1621789835172600
Starting from M2 1.4.0-alpha02 and M3 1.1.0-alpha02 you can use the minLines attribute in the Text:
Text(
text = "MinLines = 3",
modifier = Modifier.fillMaxWidth().background(Yellow),
minLines = 3
)
Note that minLines is the minimum height in terms of minimum number of visible lines. It is required that 1 <= minLines <= maxLines.
You can use it with M2 and M3.
create custom Text
it doesn't work in #Preview but the runtime
#Composable
fun MinLineText(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 0,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
var mText by remember { mutableStateOf(text) }
Text(
mText,
modifier,
color,
fontSize,
fontStyle,
fontWeight,
fontFamily,
letterSpacing,
textDecoration,
textAlign,
lineHeight,
overflow,
softWrap,
maxLines,
{
if (it.lineCount < minLines) {
mText = text + "\n".repeat(minLines - it.lineCount)
}
onTextLayout(it)
},
style,
)
}
usage
MinLineText(
text = "a sample text",
minLines = 2,
)
Is there any way to use android:autoLink feature on JetPack Compose Text?
I know, that it is maybe not "declarative way" for using this feature in one simple tag/modifier, but maybe there is some easy way for this?
For styling text I can use this way
val apiString = AnnotatedString.Builder("API provided by")
apiString.pushStyle(
style = SpanStyle(
color = Color.Companion.Blue,
textDecoration = TextDecoration.Underline
)
)
apiString.append("https://example.com")
Text(text = apiString.toAnnotatedString())
But, how can I manage clicks here? And would be great, if I programatically say what behaviour I expect from the system (email, phone, web, etc). Like it. works with TextView.
Thank you
We can achieve Linkify kind of TextView in Android Compose like this example below,
#Composable
fun LinkifySample() {
val uriHandler = UriHandlerAmbient.current
val layoutResult = remember {
mutableStateOf<TextLayoutResult?>(null)
}
val text = "API provided by"
val annotatedString = annotatedString {
pushStyle(
style = SpanStyle(
color = Color.Companion.Blue,
textDecoration = TextDecoration.Underline
)
)
append(text)
addStringAnnotation(
tag = "URL",
annotation = "https://example.com",
start = 0,
end = text.length
)
}
Text(
fontSize = 16.sp,
text = annotatedString, modifier = Modifier.tapGestureFilter { offsetPosition ->
layoutResult.value?.let {
val position = it.getOffsetForPosition(offsetPosition)
annotatedString.getStringAnnotations(position, position).firstOrNull()
?.let { result ->
if (result.tag == "URL") {
uriHandler.openUri(result.item)
}
}
}
},
onTextLayout = { layoutResult.value = it }
)
}
In the above example, we can see we give the text and also we use addStringAnnotation to set the tag. And using tapGestureFilter, we can get the clicked annotation.
Finally using UriHandlerAmbient.current we can open the link like email, phone, or web.
Reference : https://www.hellsoft.se/rendering-markdown-with-jetpack-compose/
The most important part of jetpack compose is the compatibility with native android components.
Create a component that use TextView and use it:
#Composable
fun DefaultLinkifyText(modifier: Modifier = Modifier, text: String?) {
val context = LocalContext.current
val customLinkifyTextView = remember {
TextView(context)
}
AndroidView(modifier = modifier, factory = { customLinkifyTextView }) { textView ->
textView.text = text ?: ""
LinkifyCompat.addLinks(textView, Linkify.ALL)
Linkify.addLinks(textView, Patterns.PHONE,"tel:",
Linkify.sPhoneNumberMatchFilter, Linkify.sPhoneNumberTransformFilter)
textView.movementMethod = LinkMovementMethod.getInstance()
}
}
How to use:
DefaultLinkifyText(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
text = "6999999 and https://stackoverflow.com/ works fine"
)