Poliziano
Poliziano

Reputation: 749

Prevent Text in Jetpack Compose from enlarging when device font size is increases

I have a screen in my app which displays a timer. If the user decides to increase the font size in the device settings menu then the text becomes too large for the layout and it begins to wrap. This is not an issue for my other screens that are more text heavy. For this screen - and only this screen - I would prefer to prevent the timer text from increasing in size if accessibility options are used.

Well formatted compose page Device font settings Poorly formatted compose page

The code in question looks like this, if it adds context:

HorizontalPager(state = pagerState, dragEnabled = dragEnabled) { page ->
    val timeInSeconds = abs(steps[page % steps.size] / 1000L)
    val minutes = (timeInSeconds / 60).toString().padStart(2, '0')
    val seconds = (timeInSeconds % 60).toString().padStart(2, '0')

    Text(
        modifier = Modifier.fillMaxWidth(0.85f),
        text = stringResource(R.string.pomodoro_active_notification_content_body, minutes, seconds),
        textAlign = TextAlign.Center,
        fontSize = LocalDimens.current.intervalTimeFontSize,
        style = MaterialTheme.typography.h1
    )
}

Upvotes: 24

Views: 14143

Answers (7)

thelearner
thelearner

Reputation: 126

Below is a sample, of how I am handling it in my code. Restricting font scale to not exceed the upper limit, in this case, 1.15f.

CompositionLocalProvider(
    LocalDensity provides Density(
        density = LocalDensity.current.density,
        fontScale = LocalDensity.current.fontScale.coerceIn(1f, 1.15f)
    )
) {
    // COMPOSABLE FUNCTION CALL
}

Upvotes: 1

Dwane13
Dwane13

Reputation: 408

Its pretty easy actually. You can just set new CompositionLocalProvider like this

CompositionLocalProvider(
    LocalDensity provides Density(
        LocalDensity.current.density,
        1f // - we set here default font scale instead of system one
    )
) {
    //your composables that will use no-scale
}

And please remember that text scaling exists for a purpose. Don't just wrap you whole application in this composition local. Accessibility is really important

You can check max scale for your element to work correctly and than write something like:

val myFontScale = LocalDensity.current.fontScale.coerceIn(0.75f, 1.5f)

And then simply pass this value CompositionLocal instead of 1f

Upvotes: 15

ShaatLa
ShaatLa

Reputation: 45

You can use TextUnitType.Unspecified to ignore system scale:

Text(
    text = "text",
    color = Color.White,
    fontSize = TextUnit(18f, TextUnitType.Unspecified),
)

Upvotes: 0

Phil Dukhov
Phil Dukhov

Reputation: 87605

As @CommonsWare correctly pointed out, you need to reverse scaling.

You can get fontScale from LocalDensity:

val Int.nonScaledSp
    @Composable
    get() = (this / LocalDensity.current.fontScale).sp

Usage:

10.nonScaledSp

An other option is using dp related text size: it's gonna ignore system Font Size setting, but it's gonna take Display size setting into account - it can be useful in cases when you have some container view size in dp and wanna text to have size related to the parent parent:

val density = LocalDensity.current
val textSize = with(density) { 15.dp.toSp() }

Upvotes: 43

Raed Mughaus
Raed Mughaus

Reputation: 1345

We can use CompositionLocalProvider to override the default font scale:

@Composable
fun TextWithFixedSize(
    text: String,
    fontSize: TextUnit,
) {
    val newDensity = Density(
        LocalDensity.current.density,
        fontScale = 2f,
    )
    CompositionLocalProvider(
        LocalDensity provides newDensity,
    ) {
        Text(
            text = text,
            fontSize = fontSize,
        )
    }
}

If you want to use a fixed text size on all the pages of your app, just wrap your root composable with the CompositionLocalProvider.

The same app with different font size settings:

![![enter image description here

Upvotes: 3

Thracian
Thracian

Reputation: 66526

This is an extension function for Int but you can do it for Float if you wish to

@Composable
fun Int.scaledSp(): TextUnit {
    val value: Int = this
    return with(LocalDensity.current) {
        val fontScale = this.fontScale
        val textSize =  value / fontScale
        textSize.sp
  }

Upvotes: 3

Yaroslav Yermolenko
Yaroslav Yermolenko

Reputation: 124

val textStyle = MaterialTheme.typography.h1

val fontSizeDp = with(LocalDensity.current) { textStyle.fontSize.value.dp.toSp() }
Text(
    ...
    style = textStyle,
    fontSize = fontSizeDp,
)

Upvotes: 1

Related Questions