Getting started with jetpack compose – Theming

Jetpack Compose implements Material Theming and supports a dark theme by default. You can customize color, typography, and shape theming values to fit your product’s brand, and get access to convenient functions for working with a system of dark themes (like isSystemInDarkTheme, lightColors, and darkColors).

The default MaterialTheme from the documentation looks like this:

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

We can set custom attributes:

  • colors: A complete definition of the Material Color theme for this hierarchy.
  • typography: A set of text styles to be used as this hierarchy’s typography system.
  • shapes: A set of shapes to be used by the components in this hierarchy.

Check out the example I have created for jetpack compose material theming:

jetpack theming demo

you can download this example from github.


If you’re new to Compose, I highly recommend going through the following articles:

Getting started with jetpack compose – Basic components

Getting started with jetpack compose – Layouts

Getting started with jetpack compose – Modifiers

Getting started with jetpack compose – ConstraintLayout

Getting started with jetpack compose – Scaffold layout


Let’s see the material theme attributes in detail.

Color

jetpack compose color

Jetpack Compose uses the Color class from Compose’s graphics package. We have many ways to define a color, but there are two common ways we use it more frequently.

One Way is defining the hex code of the color

val Purple200 = Color(0xFFBB86FC)

The other way is specifying the RGB values to the Color class:

val blue = Color(red = 0f, green = 0f, blue = 1f)

Since ARGB Hex is just a bunch of jargon to describe what the heck “0XFF00bc00” means, let me translate:

  • First two characters 0x tell the compiler that this is a hexadecimal number
  • The second two characters, “FF” or “DC“, represent Transparency/Opaqueness/Alpha in Hex
  • The remaining six character pairs represent Red, Green, and Blue

Create a file called something like Color.kt (the name does not matter) and fill it with immutable values:

Color.kt

import androidx.compose.ui.graphics.Color

    val Purple200 = Color(0xFFBB86FC)
    val Purple500 = Color(0xFF6200EE)
    val Purple700 = Color(0xFF3700B3)
    val Teal200 = Color(0xFF03DAC5)

Accessing the Color from the **color.kt** when required.

Text(text = "Jetpack compose" , color = Purple500)

Colors class

The colors class allows us to define different Material Design Color Systems.

class Colors(
        primary: Color,
        primaryVariant: Color,
        secondary: Color,
        secondaryVariant: Color,
        background: Color,
        surface: Color,
        error: Color,
        onPrimary: Color,
        onSecondary: Color,
        onBackground: Color,
        onSurface: Color,
        onError: Color,
        isLight: Boolean
    )
jetpack compose colors

There are around 13 attributes in the constructor of Colors. Let’s see what each attribute means.

  • Primary – color displayed most frequently across your app’s screens and components;
  • Primary Variant – color is used to distinguish elements using primary colors, such as the top app bar and the system bar.
  • Secondary – color provides more ways to accent and distinguishes your product. Having a secondary color is optional, and should be applied sparingly to accent select parts of your UI;
  • SecondaryVariant– color is used to distinguish elements using secondary colors;
  • Background – color appears behind scrollable content;
  • Surface – color uses on surfaces of components, like cards and menus;
  • Error – color used for indicating an error.
  • OnPrimary – the color of text and icons displayed on top of the primary color.
    *OnSecondary– the color of text and icons displayed on top of the secondary color;
  • OnBackground – the color of text and icons displayed on top of the background color;
  • OnSurface – the color of text and icons displayed on top of the surface color;
  • OnError – the color of text and icons displayed on top of the error color.
  • IsLight – Whether these colors are considered as a “light” or “dark” set of colors.

To make our support for DarkMode and LightMode easier, Jetpack Compose also provides two functions that generate default colors for these modes.

  • darkColors
  • lightColors
private val DarkColorPalette = darkColors(
    primary = Purple200,
    primaryVariant = Purple700,
    secondary = Teal200,
    onPrimary = Color.White,
    onSecondary = Color.Black
)

private val LightColorPalette = lightColors(
            primary = Purple500,
            primaryVariant = Purple700,
            secondary = Teal200,
            onPrimary = Color.White,
            onSecondary = Color.Black
)
Light and Dark Colors

We have successfully created colors for the Material Theme. Let’s create the Typography.

Typography

The Typograph is a class that helps us to style texts. With the help of the Typograph class, we can customize FontFamily, FontStyle, FontWeight, LetterSpacing, TextDecoration, Color, and many more attributes related to texts.

class Typography(
        defaultFontFamily: FontFamily = FontFamily.Default,
        h1: TextStyle,
        h2: TextStyle,
        h3: TextStyle,
        h4: TextStyle,
        h5: TextStyle,
        h6: TextStyle,
        subtitle1: TextStyle,
        subtitle2: TextStyle,
        body1: TextStyle,
        body2: TextStyle,
        button: TextStyle,
        caption: TextStyle,
        overline: TextStyle
    )
jetpack compose typography

In addition to this, we should configure a Typography object with the following parameters:

  • h1 is the largest headline, reserved for short and important text.
  • h2 is the second-largest headline, reserved for short and important text.
  • h3 is the third-largest headline, reserved for short and important text.
  • h4 is the fourth-largest headline, reserved for short and important text.
  • h5 is the fifth-largest headline, reserved for short and important text.
  • h6 is the sixth-largest headline, reserved for short and important text.
  • subtitle1 is the largest subtitle and is typically reserved for medium-emphasis text that is shorter in length.
  • subtitle2 is the smallest subtitle and is typically reserved for medium-emphasis text that is shorter in length.
  • body1 is the largest body and is typically reserved for a long-form text that is shorter in length.
  • body2 is the smallest body and is typically reserved for a long-form text that is shorter in length.
  • the button is reserved for a button text.
  • the caption is one of the smallest font sizes it is reserved for annotating imagery or introducing a headline.
  • overline is one of the smallest font sizes.

We can override the needed font Style like h1, body1, button, etc as below,

Type.kt

val Typography = Typography(
        body1 = TextStyle(
            fontFamily = FontFamily.Default,
            fontWeight = FontWeight.Normal,
            fontSize = 16.sp
        ),
        button = TextStyle(
            fontFamily = FontFamily.Default,
            fontWeight = FontWeight.W500,
            fontSize = 14.sp
        ),
        caption = TextStyle(
            fontFamily = FontFamily.Default,
            fontWeight = FontWeight.Normal,
            fontSize = 12.sp
        )

    )

The example I have created,

@Composable
fun TypographyPreview() {
    Column(Modifier.width(400.dp).padding(10.dp)) {
        Text(
            text = "H2 ",
            style = MaterialTheme.typography.h2
        ) Spacer (modifier = Modifier.padding(4.dp)) Text (text =
            "Subtitle 1 " , style = MaterialTheme.typography.subtitle1)        
        Spacer(modifier = Modifier.padding(4.dp))        
        Text(text = "Body 1 ", style = MaterialTheme.typography.body1)        
        Spacer(modifier = Modifier.padding(4.dp))        
        Text(text = "Button ", style = MaterialTheme.typography.button)        
        Spacer(modifier = Modifier.padding(4.dp))        
        Text(text = "Caption ", style = MaterialTheme.typography.caption)
    }
}
Typography

Done with the Typography. finally, create the shape.

Shape

jetpack compose shapes

Creating shapes with Jetpack Compose is super easy. Similar to how we created the Typography instance, we also create the instance of Shapes.

Shape.kt

import androidx.compose.foundation.shape.RoundedCornerShape
    import androidx.compose.material.Shapes
    import androidx.compose.ui.unit.dp

    val Shapes = Shapes(
        small = RoundedCornerShape(4.dp),
        medium = RoundedCornerShape(4.dp),
        large = RoundedCornerShape(0.dp)
    )

Let’s Create a Material Theme

By using the instances of colors, typography, and shapes above, we can make a material theme that can be used across the application.

@Composable
fun JetpackComposeThemingTheme(colors: Colors, 
                               content: @Composable() () -> Unit) {
    MaterialTheme(colors = colors, 
        typography = Typography, 
        shapes = Shapes, 
        content = content)
}

CustomComposeTheme is a composable function with two attributes:

isDarkTheme — To set the dark or light color scheme based on checking isSystemInDarkTheme.
content — A composable function where the MaterialTheme is to be used.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) 
        setContent { 
            MaterialTheme { 
                Column { 
                    Text(text = "Jetpack Compose") 
                } 
            } 
        }
    }
}

Dark theme

Material composable that makes use of a Surface (like Card, TopAppBar, etc.) automatically includes dark theme properties like desaturated colors for accessibility, elevation overlays, and limited color accents. You can also incorporate these in custom scenarios.

isSystemInDarkTheme() is a call that asks any compatible Android device for the user’s preference for a light or dark theme.

It returns a boolean value which we can use in a Ternary (Conditional) Assignment expression such as

colors = if (darkTheme) DarkColorPalette else LightColorPalette

code to add the dark mode in the theme,

@Composable
fun PurpleTheme(darkTheme: Boolean = isSystemInDarkTheme(), 
                content: @Composable() () -> Unit) {
    val colors = if (darkTheme) DarkColorPalette else LightColorPalette
    MaterialTheme (colors =
        colors, typography = Typography, shapes = Shapes, content = content)
}

Material Icons

Jetpack Compose also provides a convenient means of using icons listed in the Material Icons tool, including all icon styles—filled, outlined, rounded, two-tone, and sharp. With this artifact, you can use icons directly, without the need to download SVGs and convert them to VectorDrawables in Android Studio.

Add dependency in the module build.gradle

implementation "androidx.compose.material:material-icons-extended:$compose_version"

You can get the icon by,

Icon(Icons.Default.Add) // Default == Filled
    Icon(Icons.Outlined.Notifications)
    Icon(Icons.Rounded.ArrowForward)
    Icon(Icons.TwoTone.Menu)
    Icon(
       Icons.Sharp.Edit,
       tint = MaterialTheme.colors.primary
    )

That’s all about jetpack compose theming.

You can download this example from Github.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *


Latest Posts