I just started to play with Compose for the first time, and almost immediately found an interesting discrepancy that I wasn’t expecting.

I built a very simple screen consisting of two lines of text, one for a text description and one for a hyperlink. My code is below. I am using a Text composable for the text description and a ClickableText composable for the hyperlink.

@Composable
fun ComposeExample() {
    Column(
        modifier = Modifier
            .fillMaxWidth(1f)
            .fillMaxHeight(1f),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Click link below:"
        )
        HyperlinkText()
    }
}

@Composable
fun HyperlinkText() {
    val annotatedText = buildAnnotatedString {
        ... // set up hyperlink
    }
    
    val uriHandler = LocalUriHandler.current

    ClickableText(
        text = annotatedText,
        onClick = { offset ->
            annotatedText.getStringAnnotations(
                tag = "URL", start = offset, end = offset
            )
                .firstOrNull()?.let { annotation ->
                    uriHandler.openUri(annotation.item)
                }
        }
    )
}

And this is what the screen looks like. Do you notice the discrepancy between the two lines?

Yes, the font sizes look different! Since I did not specify the font size for either composable, this must be because the default font sizes are not the same for Text and ClickableText. But why? I decided to dive deep into the documentation to figure this out.

Where do these composables come from?

Based on the name, I had naively assumed that ClickableText was simply a wrapper around Text with added hyperlink capability, and therefore their default font sizes would be the same. Incorrect!

ClickableText is part of the Compose Foundations library, which provides basic components without any theming. Text, on the other hand, comes from the Compose Material library, which offers components based on material theming.

At this point I suspected the fact that Text is a material component while ClickableText is not was the key to understanding the font size discrepancy.

Where are their default font sizes set?

The next question is where are the default font sizes being set? I wanted to confirm I was indeed spotting a difference in sizes by digging into the Compose core and Material source code.

First up, ClickableText:

  • ClickableText extends BasicText, passing in TextStyle.Default as the text style.
  • BasicText in turn extends CoreText.
  • CoreText uses TextDelegate to manage the text style.
  • TextDelegate sets the styling of its text using MultiParagraphIntrinsics with style calculated by the function resolveDefaults().
  • If the style font size is unspecified (which is the case for TextStyle.Default), resolveDefaults() returns DefaultFontSize.
  • And last but not least DefaultFontSize is 14.sp.

Now, Text:

  • Text’s style is LocalTextStyle.current. Note that Text also extends BasicText but doesn’t use its styling.
  • The value of LocalTextStyle is set by the function ProvideTextStyle(), which provides a CompositionLocal.
  • Since Text uses the Material theme, we can look there to find the text style used by CompositionLocal: typography.body1.
  • And finally typography.body1 has a font size of 16.sp.

Mystery solved!

To summarize, Text receives the Material theme’s default font size from the Material theme via CompositionLocal, whereas ClickableText uses a non-material default font size coming from TextDelegate. And these defaults do not match. So my suspicion that the crux of the matter is material vs. non-material components was correct.

Follow-up questions

This adventure leads me to wonder what Google’s plans are for ClickableText. Is the intention to make a material version in the future? Or is the lack of a material ClickableText a hint to avoid using hyperlinks in our apps? Please comment if you have any insights!