Search code examples
android-jetpack-composematerial-designandroid-jetpack-compose-material3

Material 3 custom colors - How to generate tonal palettes?


The material 3 site has a section on custom colors.

tonal palettes Theme builder

Is there a way to programmatically generate tonal palettes for a given (harmonized) color to override the color scheme?

@Composable
fun CardMinimalExample(
    customColor: Color = Color.Red
) {
    MaterialTheme(
        colorScheme = MaterialTheme.colorScheme.copy(
            surfaceVariant = customColor
        )
    ) { // inefficient? 
        Card() {
            Text(text = "Hello, world!")
        }
    }
}


@Preview
@Composable
private fun Preview() {
    CardMinimalExample()
}

Solution

  • I found the following restricted method Hct.fromInt that seems to work.

    @SuppressLint("RestrictedApi")
    @ColorInt
    fun getColorRole(@ColorInt color: Int, @IntRange(from = 0, to = 100) tone: Int): Int {
        val hctColor = com.google.android.material.color.utilities.Hct.fromInt(color)
        hctColor.tone = tone.toDouble()
        return hctColor.toInt()
    }
    
        val hexColor = java.lang.String.format("#%08X", -0x1 and color.toArgb())
        Toast
            .makeText(
                context,
                hexColor,
                Toast.LENGTH_SHORT
            )
            .show()
    

    Here is a library built on this method.

    data class CustomColorScheme(
        val color: Color,
        val onColor: Color,
        val colorContainer: Color,
        val onColorContainer: Color
    )
    
    fun Color.lightCustomColorScheme(): CustomColorScheme {
        val palette = tonalPalette()
        return CustomColorScheme(
            color = palette.color40,
            onColor = palette.color100,
            colorContainer = palette.color90,
            onColorContainer = palette.color10
        )
    }
    
    fun Color.darkCustomColorScheme(): CustomColorScheme {
        val palette = tonalPalette()
        return CustomColorScheme(
            color = palette.color80,
            onColor = palette.color20,
            colorContainer = palette.color30,
            onColorContainer = palette.color90
        )
    }