Mohamed Hassan
Mohamed Hassan

Reputation: 211

How to change font family in Jetpack Compose programmatically?

I'm building an Android app and I wanted to add an option in the settings for the user to choose either he wants to use The app custom font family or just use the dafault system font family and maybe also options for other fonts. Is there an option in compose to do this?

Upvotes: 2

Views: 3852

Answers (3)

Last Noob Ninja
Last Noob Ninja

Reputation: 71

If you are using androidx.compose.material3.Typography, there would be 2 approaches: First one is to copy each style and change the font family.

fun createTypography(parent: Typography, fontFamily: FontFamily): Typography{
return Typography(
    parent.displayLarge.copy(fontFamily = fontFamily),
    parent.displayMedium.copy(fontFamily = fontFamily),
    parent.displaySmall.copy(fontFamily = fontFamily),
    parent.headlineLarge.copy(fontFamily = fontFamily),
    parent.headlineMedium.copy(fontFamily = fontFamily),
    parent.headlineSmall.copy(fontFamily = fontFamily),
    parent.titleLarge.copy(fontFamily = fontFamily),
    parent.titleMedium.copy(fontFamily = fontFamily),
    parent.titleSmall.copy(fontFamily = fontFamily),
    parent.bodyLarge.copy(fontFamily = fontFamily),
    parent.bodyMedium.copy(fontFamily = fontFamily),
    parent.bodySmall.copy(fontFamily = fontFamily),
    parent.labelLarge.copy(fontFamily = fontFamily),
    parent.labelMedium.copy(fontFamily = fontFamily),
    parent.labelSmall.copy(fontFamily = fontFamily)
    )    
}

the other way is to use reflection:

fun createTypographyReflection(parent: Typography, fontFamily: FontFamily): Typography{
val result = Typography()
Typography::class.java.declaredFields.filter { it.type == TextStyle::class.java }.forEach { field ->
    field.isAccessible = true
    val textStyle = field.get(parent) as TextStyle
    val modifiedTextStyle = textStyle.copy(fontFamily = fontFamily)
    field.set(result, modifiedTextStyle)
}
return result
}

Upvotes: 0

Mahmudul Hasan Shohag
Mahmudul Hasan Shohag

Reputation: 3131

For Material3 (Material Design 3):

Note: Unlike the M2 Typography class, the M3 Typography class doesn’t currently include a defaultFontFamily parameter. You’ll need to use the fontFamily parameter in each of the individual TextStyles instead. (Source)

So, here is an example of setting the default font-family in M3:

// Type.kt

import androidx.compose.material3.Typography
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import com.example.exampleapp.R

// Declare the font families
object AppFont {
    val TitilliumWeb = FontFamily(
        Font(R.font.titillium_web_regular),
        Font(R.font.titillium_web_italic, style = FontStyle.Italic),
        Font(R.font.titillium_web_medium, FontWeight.Medium),
        Font(R.font.titillium_web_medium_italic, FontWeight.Medium, style = FontStyle.Italic),
        Font(R.font.titillium_web_bold, FontWeight.Bold),
        Font(R.font.titillium_web_bold_italic, FontWeight.Bold, style = FontStyle.Italic)
    )
}

private val defaultTypography = Typography()
val Typography = Typography(
    displayLarge = defaultTypography.displayLarge.copy(fontFamily = AppFont.TitilliumWeb),
    displayMedium = defaultTypography.displayMedium.copy(fontFamily = AppFont.TitilliumWeb),
    displaySmall = defaultTypography.displaySmall.copy(fontFamily = AppFont.TitilliumWeb),

    headlineLarge = defaultTypography.headlineLarge.copy(fontFamily = AppFont.TitilliumWeb),
    headlineMedium = defaultTypography.headlineMedium.copy(fontFamily = AppFont.TitilliumWeb),
    headlineSmall = defaultTypography.headlineSmall.copy(fontFamily = AppFont.TitilliumWeb),

    titleLarge = defaultTypography.titleLarge.copy(fontFamily = AppFont.TitilliumWeb),
    titleMedium = defaultTypography.titleMedium.copy(fontFamily = AppFont.TitilliumWeb),
    titleSmall = defaultTypography.titleSmall.copy(fontFamily = AppFont.TitilliumWeb),

    bodyLarge = defaultTypography.bodyLarge.copy(fontFamily = AppFont.TitilliumWeb),
    bodyMedium = defaultTypography.bodyMedium.copy(fontFamily = AppFont.TitilliumWeb),
    bodySmall = defaultTypography.bodySmall.copy(fontFamily = AppFont.TitilliumWeb),

    labelLarge = defaultTypography.labelLarge.copy(fontFamily = AppFont.TitilliumWeb),
    labelMedium = defaultTypography.labelMedium.copy(fontFamily = AppFont.TitilliumWeb),
    labelSmall = defaultTypography.labelSmall.copy(fontFamily = AppFont.TitilliumWeb)
)
// Theme.kt

import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable

@Composable
fun AppTheme(
    content: @Composable () -> Unit
) {
    MaterialTheme(
        typography = Typography,
        content = content
    )
}

Example for an individual component:

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import com.example.exampleapp.theme.AppFont

@Composable
fun ExampleScreen() {
    Text(
        text = "Hello World!", 
        fontFamily = AppFont.TitilliumWeb
    )
}

Upvotes: 7

Mohamed Hassan
Mohamed Hassan

Reputation: 211

I was able to do it by using the defaultFontFamily parameter, adding an extra parameter to MyAppTheme Composable and passing the typography object to the MaterialTheme() constructor instead of the default one as you can see the code below :

fun getTypography(fontFamily: FontFamily) = Typography(
    defaultFontFamily = fontFamily
)

In Theme.kt :

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Added fontFamily param
    fontFamily: FontFamily = Rubik,
    content: @Composable () -> Unit) {

    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    val typography = getTypography(fontFamily)

    MaterialTheme(
        colors = colors,
        typography = typography,
        shapes = Shapes,
        content = content
    )
}

Upvotes: 1

Related Questions