Search code examples
androidandroid-jetpack-composeandroid-jetpack-compose-material3

Surface content colour slightly different to expected


My Text colour is not the same as that defined in the theme despite it being inside Surface; it appears almost the same but not quite.

enter image description here

This simplistic sample layout is bare bones:

MyTheme {
    Surface {
        Column(Modifier.padding(12.dp)) {
            Text(
                text = "This is a line of text, uses surface",
            )
            Text(
                text = "This is a line of text, forced white",
                color = Color.White,
                modifier = Modifier.padding(top = 4.dp)
            )
        }
    }
}

My theme is simple:

@Composable
fun MyTheme(
    content: @Composable () -> Unit,
) {
    MaterialTheme(
        colors = DarkColours,
        ...,
        content = content
    )
}

internal val DarkColours = darkColors(
    surface = Color(0xFF043143),
    onSurface = Color.White,
    ... // None of the other colours are close to off-white
)

Even if I explicitly specify the colours to use in the surface, the top Text is still off-white (no change):

Surface(
    color = Color(0xFF043143),
    contentColor = Color.White
) { ... }

Elevation is 0.0dp and uses DefaultElevationOverlay so shouldn't have any effect on contentColor

LocalContentColor.current reports Color(1.0, 1.0, 1.0) when printed by the first Text.


Solution

  • Figured it out while typing this question up so figured I'd share it.

    LocalContentAlpha was set to 0.87 inside the contents of MaterialTheme. This is because of its line

    LocalContentAlpha provides ContentAlpha.high,
    

    which ultimately resolves to this due to the fact that I'm using a material Color scheme with isLight = false (thanks to darkColors).

    private object LowContrastContentAlpha {
        const val high: Float = 0.87f
        const val medium: Float = 0.60f
        const val disabled: Float = 0.38f
    }
    

    A workaround is to re-set alpha back to 1.0:

    MaterialTheme(
        colors = DarkColours,
        ...
    ) {
        CompositionLocalProvider(
            // Undo our "dark" colours triggering Material theme
            // to use a low contrast alpha (87%).
            LocalContentAlpha provides 1.0f,
            content = content
        )
    }
    

    (or just use lightColors / set isLight = true if you can afford to do this)

    This kind of blows my mind, that Material would just change the alpha of literally all of my app's content - I can't find any doc on why it does this either in the material spec!