Trabalhar com fontes

Esta página descreve como definir fontes no app Compose.

Definir fonte

Text tem um parâmetro fontFamily para permitir a configuração da fonte usada no elemento combinável. Por padrão, as famílias de fontes Serif, Sans Serif, fontes monoespaçadas e cursivas estão incluídas:

@Composable
fun DifferentFonts() {
    Column {
        Text("Hello World", fontFamily = FontFamily.Serif)
        Text("Hello World", fontFamily = FontFamily.SansSerif)
    }
}

As palavras

É possível usar o atributo fontFamily para trabalhar com fontes personalizadas definidas na pasta res/font:

Representação gráfica da pasta "res > font" do ambiente de desenvolvimento

O exemplo abaixo mostra como definir uma fontFamily com base nesses arquivos de fonte e como usar a função Font:

val firaSansFamily = FontFamily(
    Font(R.font.firasans_light, FontWeight.Light),
    Font(R.font.firasans_regular, FontWeight.Normal),
    Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
    Font(R.font.firasans_medium, FontWeight.Medium),
    Font(R.font.firasans_bold, FontWeight.Bold)
)

É possível transmitir essa fontFamily para o elemento combinável Text. Como uma fontFamily pode incluir pesos diferentes, é possível definir o fontWeight manualmente para selecionar o peso certo para o texto:

Column {
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Light)
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
    Text(
        text = "text",
        fontFamily = firaSansFamily,
        fontWeight = FontWeight.Normal,
        fontStyle = FontStyle.Italic
    )
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Medium)
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}

As palavras

Para aprender a definir a tipografia em todo o app, consulte Sistemas de design personalizados no Compose.

Fontes para download

Como iniciar no Compose 1.2.0, você pode usar a API de fontes para download no app Compose para fazer o download da API de fontes do fontes de forma assíncrona e usá-las no seu app.

No momento, não há suporte para fontes para download fornecidas por provedores personalizados.

Usar fontes para download de maneira programática

Para fazer o download de uma fonte de forma programática no app, siga estas etapas:

  1. Adicione a dependência:

    Groovy

    dependencies {
        ...
        implementation "androidx.compose.ui:ui-text-google-fonts:1.7.8"
    }

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.7.8")
    }
  2. Inicialize a GoogleFont.Provider com as credenciais da biblioteca Google Fonts:
    val provider = GoogleFont.Provider(
        providerAuthority = "com.google.android.gms.fonts",
        providerPackage = "com.google.android.gms",
        certificates = R.array.com_google_android_gms_fonts_certs
    )
    Os parâmetros que o provedor recebe são:
    • A autoridade de provedor de fontes para a biblioteca Google Fonts.
    • O pacote de provedor de fontes para confirmar a identidade do provedor.
    • Uma lista de conjuntos de hashes para que os certificados confirmem a identidade do provedor. É possível encontrar os hashes necessários para o provedor do Google Fonts no arquivo font_certs.xml do App de exemplo Jetchat.
  3. Defina um FontFamily:
    // ...
     import androidx.compose.ui.text.googlefonts.GoogleFont
     import androidx.compose.ui.text.font.FontFamily
     import androidx.compose.ui.text.googlefonts.Font
     // ...
    
    val fontName = GoogleFont("Lobster Two")
    
    val fontFamily = FontFamily(
        Font(googleFont = fontName, fontProvider = provider)
    )
    Você pode consultar outros parâmetros para sua fonte, como peso e estilo, usando FontWeight e FontStyle, respectivamente:
    // ...
     import androidx.compose.ui.text.googlefonts.GoogleFont
     import androidx.compose.ui.text.font.FontFamily
     import androidx.compose.ui.text.googlefonts.Font
     // ...
    
    val fontName = GoogleFont("Lobster Two")
    
    val fontFamily = FontFamily(
        Font(
            googleFont = fontName,
            fontProvider = provider,
            weight = FontWeight.Bold,
            style = FontStyle.Italic
        )
    )
  4. Configure a FontFamily que você quer usar na função combinável Text:

Text(
    fontFamily = fontFamily, text = "Hello World!"
)

Também é possível definir Tipografia para usar a FontFamily:

val MyTypography = Typography(
    bodyMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 12.sp/*...*/
    ),
    bodyLarge = TextStyle(
        fontFamily = fontFamily,
        fontWeight = FontWeight.Bold,
        letterSpacing = 2.sp,
        /*...*/
    ),
    headlineMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.SemiBold/*...*/
    ),
    /*...*/
)

Em seguida, defina a tipografia para o tema do seu app:

MyAppTheme(
    typography = MyTypography
)/*...*/

Para conferir um exemplo de app que está implementando fontes para download no Compose com o Material3, consulte o app de exemplo Jetchat (em inglês).

Adicionar fontes substitutas

É possível determinar uma cadeia de fontes substitutas que vão ser usadas caso o download da fonte não seja concluído corretamente. Por exemplo, se você tiver definido a fonte para download assim:

// ...
 import androidx.compose.ui.text.googlefonts.Font
 // ...

val fontName = GoogleFont("Lobster Two")

val fontFamily = FontFamily(
    Font(googleFont = fontName, fontProvider = provider),
    Font(googleFont = fontName, fontProvider = provider, weight = FontWeight.Bold)
)

Você pode definir os padrões da fonte para os dois pesos:

// ...
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.googlefonts.Font
 // ...

val fontName = GoogleFont("Lobster Two")

val fontFamily = FontFamily(
    Font(googleFont = fontName, fontProvider = provider),
    Font(resId = R.font.my_font_regular),
    Font(googleFont = fontName, fontProvider = provider, weight = FontWeight.Bold),
    Font(resId = R.font.my_font_regular_bold, weight = FontWeight.Bold)
)

Verifique se você está adicionando as importações corretas.

Essa definição cria uma FontFamily contendo duas cadeias, uma para cada peso. O mecanismo de carregamento tentará primeiro resolver a fonte on-line, e a fonte localizada na pasta de recursos R.font local.

Depurar sua implementação

Para verificar se o download da fonte está sendo feito corretamente, defina um gerenciador de corrotina de depuração. Ele fornece o comportamento do que fazer caso a fonte não carregue de forma assíncrona.

Comece criando um CoroutineExceptionHandler:

val handler = CoroutineExceptionHandler { _, throwable ->
    // process the Throwable
    Log.e(TAG, "There has been an issue: ", throwable)
}

Transmita-a para o método createFontFamilyResolver para que o resolvedor use o novo gerenciador:

CompositionLocalProvider(
    LocalFontFamilyResolver provides createFontFamilyResolver(LocalContext.current, handler)
) {
    Column {
        Text(
            text = "Hello World!", style = MaterialTheme.typography.bodyMedium
        )
    }
}

Você também pode usar a API isAvailableOnDevice do provedor para testar se ele está disponível e se os certificados estão configurados corretamente. Para fazer isso, chame o método isAvailableOnDevice, que vai retornar um valor falso se o provedor estiver configurado incorretamente.

val context = LocalContext.current
LaunchedEffect(Unit) {
    if (provider.isAvailableOnDevice(context)) {
        Log.d(TAG, "Success!")
    }
}

Avisos

A biblioteca Google Fonts leva vários meses para disponibilizar novas fontes no Android. Há um intervalo entre quando uma fonte é adicionada em fonts.google.com e o momento em que ela é disponibilizada pela API de fontes para download no sistema de visualização ou no Compose. Recém-chegado fontes adicionadas podem não ser carregadas no aplicativo com uma IllegalStateException Para ajudar os desenvolvedores a identificar esse erro em relação a outros tipos de erros de carregamento de fonte, adicionamos mensagens descritivas para a exceção no Compose com as mudanças aqui. Se você encontrar problemas, informe-os usando o formulário de rastreador.

Usar fontes variáveis

Uma fonte variável é um formato que permite que um arquivo de fonte contenha estilos diferentes. Com fontes variáveis, é possível modificar eixos (ou parâmetros) para gerar seu estilo preferido. Esses eixos podem ser padrão, como peso, largura, inclinação, e itálico ou personalizado, que diferem entre fontes variáveis.

Cinco configurações da mesma fonte de variável com valores de eixo diferentes.
Figura 1. Texto usando a mesma fonte variável personalizada com diferentes valores de eixo.

Usar fontes variáveis em vez de arquivos de fontes regulares permite que você tenha apenas uma arquivo de fonte em vez de várias.

Para mais informações sobre fontes variáveis, acesse Fontes do Google Conhecimento, todo o catálogo de fontes variáveis e uma tabela dos eixos suportados para cada fonte.

Este documento mostra como implementar uma fonte variável no app Compose.

Carregar uma fonte variável

  1. Faça o download da fonte da variável que quer usar (por exemplo, Roboto Flex) e coloque-o na pasta app/res/font do app. Verifique se o arquivo .O arquivo ttf que você adicionar é a versão variável da fonte e o nome do arquivo da fonte é todo em letras minúsculas e não contém caracteres especiais.

  2. Para carregar uma fonte variável, defina um FontFamily usando a fonte colocada no Diretório res/font/:

    // In Typography.kt
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily =
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(950),
                    FontVariation.width(30f),
                    FontVariation.slant(-6f),
                )
            )
        )

    A API FontVariation permite configurar eixos de fonte padrão, como weight, width e slant. Esses são eixos padrão que são disponível com qualquer fonte variável. É possível criar diferentes configurações do com base no local em que ela será usada.

  3. As fontes variáveis estão disponíveis apenas para versões do Android O e mais recentes. Portanto, adicione um limite e configure um substituto adequado:

    // In Typography.kt
    val default = FontFamily(
        /*
        * This can be any font that makes sense
        */
        Font(
            R.font.robotoflex_static_regular
        )
    )
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(950),
                    FontVariation.width(30f),
                    FontVariation.slant(-6f),
                )
            )
        )
    } else {
        default
    }

  4. Extraia as configurações em um conjunto de constantes para facilitar a reutilização e substitua as configurações de fonte com estas constantes:

    // VariableFontDimension.kt
    object DisplayLargeVFConfig {
        const val WEIGHT = 950
        const val WIDTH = 30f
        const val SLANT = -6f
        const val ASCENDER_HEIGHT = 800f
        const val COUNTER_WIDTH = 500
    }
    
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(DisplayLargeVFConfig.WEIGHT),
                    FontVariation.width(DisplayLargeVFConfig.WIDTH),
                    FontVariation.slant(DisplayLargeVFConfig.SLANT),
                )
            )
        )
    } else {
        default
    }

  5. Configure a tipografia do Material Design 3 para usar o FontFamily:

    // Type.kt
    val Typography = Typography(
        displayLarge = TextStyle(
            fontFamily = displayLargeFontFamily,
            fontSize = 50.sp,
            lineHeight = 64.sp,
            letterSpacing = 0.sp,
            /***/
        )
    )

    Este exemplo usa a tipografia do Material 3 (link em inglês) displayLarge, que tem diferentes as configurações de fonte padrão e os usos recomendados. Por exemplo, use displayLarge para textos curtos e importantes, já que é o texto mais longo na tela.

    Com o Material 3, é possível mudar os valores padrão de TextStyle e fontFamily para personalizar a tipografia. No snippet acima, você configura instâncias de TextStyle para personalizar as configurações de fonte de cada família.

  6. Agora que você definiu a tipografia, transmita-a ao MaterialTheme do M3:

    MaterialTheme(
        colorScheme = MaterialTheme.colorScheme,
        typography = Typography,
        content = content
    )

  7. Por fim, use um elemento combinável Text e especifique o estilo para um dos estilos de tipografia definidos, MaterialTheme.typography.displayLarge:

    @Composable
    @Preview
    fun CardDetails() {
        MyCustomTheme {
            Card(
                shape = RoundedCornerShape(8.dp),
                elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
            ) {
                Column(
                    modifier = Modifier.padding(16.dp)
                ) {
                    Text(
                        text = "Compose",
                        style = MaterialTheme.typography.displayLarge,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 1
                    )
                    Text(
                        text = "Beautiful UIs on Android",
                        style = MaterialTheme.typography.headlineMedium,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 2
                    )
                    Text(
                        text = "Jetpack Compose is Android’s recommended modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.",
                        style = MaterialTheme.typography.bodyLarge,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 3
                    )
                }
            }
        }
    }

    Cada elemento combinável Text é configurado pelo estilo do tema do Material Design e contém uma configuração de fonte variável diferente. É possível usar MaterialTheme.typography para recuperar a tipografia fornecida ao elemento combinável MaterialTheme do M3.

Três textos diferentes, todos mostrando diferentes configurações de fonte.
Figura 2. Fonte variável aplicada em três configurações diferentes.

Usar eixos personalizados

As fontes também podem ter eixos personalizados. Elas são definidas no próprio arquivo de fonte. Por exemplo, a fonte Roboto Flex tem o eixo de altura do ascendente ("YTAS"), que ajusta a altura dos ascendentes minúsculos, enquanto a largura do contador ("XTRA") ajusta a largura de cada letra.

É possível mudar o valor desses eixos com as configurações FontVariation.

Para mais informações sobre os eixos personalizados que podem ser configurados para uma fonte, consulte a tabela de eixos compatíveis de cada uma delas.

  1. Para usar eixos personalizados, defina funções para os eixos ascenderHeight e counterWidth personalizados:

    fun ascenderHeight(ascenderHeight: Float): FontVariation.Setting {
        require(ascenderHeight in 649f..854f) { "'Ascender Height' must be in 649f..854f" }
        return FontVariation.Setting("YTAS", ascenderHeight)
    }
    
    fun counterWidth(counterWidth: Int): FontVariation.Setting {
        require(counterWidth in 323..603) { "'Counter width' must be in 323..603" }
        return FontVariation.Setting("XTRA", counterWidth.toFloat())
    }

    Essas funções fazem o seguinte:

    • Defina limites para os valores que eles podem aceitar. Como você pode ver no catálogo de fontes variáveis, ascenderHeight (YTAS) tem um valor mínimo de 649f e um máximo de 854f.
    • Retorne a configuração da fonte para que a configuração esteja pronta para ser adicionada à fonte. Em método FontVariation.Setting(), o nome do eixo (YTAS, XTRA) será fixado no código e aceita o valor como parâmetro.
  2. Usando os eixos com a configuração da fonte, transmita parâmetros adicionais para cada Font carregado:

    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(DisplayLargeVFConfig.WEIGHT),
                    FontVariation.width(DisplayLargeVFConfig.WIDTH),
                    FontVariation.slant(DisplayLargeVFConfig.SLANT),
                    ascenderHeight(DisplayLargeVFConfig.ASCENDER_HEIGHT),
                    counterWidth(DisplayLargeVFConfig.COUNTER_WIDTH)
                )
            )
        )
    } else {
        default
    }

    A altura das ascendentes em letras minúsculas agora aumentou, e o outro texto ficou mais largo:

Três textos diferentes mostrando configurações diferentes de fontes variáveis, com eixos personalizados definidos. Alguns têm ascendentes menores e são mais largos do que antes.
Figura 3. Texto mostrando eixos personalizados definidos em fontes variáveis.

Outros recursos

Para mais informações, consulte a seguinte postagem do blog sobre fontes variáveis: