DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Material Design 3 Theming in Jetpack Compose: The Complete Guide

Material Design 3 Theming in Jetpack Compose: The Complete Guide

Material Design 3 represents the latest evolution of Google's design system, bringing modern aesthetics and powerful theming capabilities to Android applications. When combined with Jetpack Compose, Material Design 3 offers developers an elegant, declarative way to build beautiful, consistent user interfaces. In this comprehensive guide, we'll explore every aspect of Material Design 3 theming in Jetpack Compose, from basic color schemes to advanced dynamic color implementation.

Understanding Material Design 3 Color System

Material Design 3 introduces a refined color system based on the principles of the CAM16 color space, which better aligns with human perception. The primary components of the color system are:

Primary Color

The primary color is the most prominent color in your app's color scheme. It typically represents your brand and is used for key interactive elements like buttons, links, and navigation items.

// In your Color.kt file
val md_theme_light_primary = Color(0xFF006874)
val md_theme_dark_primary = Color(0xFF4FD8EB)
Enter fullscreen mode Exit fullscreen mode

Secondary Color

The secondary color provides an accent that complements your primary color. It's often used for secondary interactive elements and helps create visual hierarchy.

val md_theme_light_secondary = Color(0xFF4A6363)
val md_theme_dark_secondary = Color(0xFFB1CCCC)
Enter fullscreen mode Exit fullscreen mode

Tertiary Color

The tertiary color is an alternative accent color that provides even more flexibility in your design system. It's useful for highlighting less critical information or providing additional visual variety.

val md_theme_light_tertiary = Color(0xFF006B5E)
val md_theme_dark_tertiary = Color(0xFF52D9C4)
Enter fullscreen mode Exit fullscreen mode

Setting Up Your Color Scheme

A complete Material Design 3 color scheme includes far more than just primary, secondary, and tertiary colors. It also includes neutral colors, container variants, and on-color variants for text and icons.

// Complete theme setup
data class ColorScheme(
    val primary: Color,
    val onPrimary: Color,
    val primaryContainer: Color,
    val onPrimaryContainer: Color,
    val secondary: Color,
    val onSecondary: Color,
    val secondaryContainer: Color,
    val onSecondaryContainer: Color,
    val tertiary: Color,
    val onTertiary: Color,
    val tertiaryContainer: Color,
    val onTertiaryContainer: Color,
    val error: Color,
    val onError: Color,
    val errorContainer: Color,
    val onErrorContainer: Color,
    val background: Color,
    val onBackground: Color,
    val surface: Color,
    val onSurface: Color,
    val surfaceVariant: Color,
    val onSurfaceVariant: Color,
    val outline: Color,
    val outlineVariant: Color,
    val scrim: Color
)
Enter fullscreen mode Exit fullscreen mode

Implementing Dark Mode Support

Dark mode is not just a cosmetic feature—it's essential for modern app development. Material Design 3 provides separate color schemes for light and dark modes, allowing your app to adapt seamlessly based on system preferences.

private val LightColorScheme = lightColorScheme(
    primary = md_theme_light_primary,
    secondary = md_theme_light_secondary,
    tertiary = md_theme_light_tertiary,
    background = md_theme_light_background,
    surface = md_theme_light_surface,
    onBackground = md_theme_light_onBackground,
    onSurface = md_theme_light_onSurface,
    // ... other colors
)

private val DarkColorScheme = darkColorScheme(
    primary = md_theme_dark_primary,
    secondary = md_theme_dark_secondary,
    tertiary = md_theme_dark_tertiary,
    background = md_theme_dark_background,
    surface = md_theme_dark_surface,
    onBackground = md_theme_dark_onBackground,
    onSurface = md_theme_dark_onSurface,
    // ... other colors
)

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}
Enter fullscreen mode Exit fullscreen mode

Dynamic Color on Android 12+

Android 12 introduced dynamic theming, which extracts colors from the user's wallpaper to create a personalized color scheme. Material Design 3 in Jetpack Compose provides seamless support for this feature.

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }
        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}
Enter fullscreen mode Exit fullscreen mode

When users enable dynamic color in their system settings, their device's wallpaper colors automatically influence your app's appearance. This creates a personalized, harmonious experience without requiring any additional work from developers.

Typography System

Material Design 3 defines a comprehensive typography system with multiple scales for different text purposes.

val Typography = Typography(
    displayLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W400,
        fontSize = 57.sp,
        lineHeight = 64.sp,
        letterSpacing = 0.sp
    ),
    displayMedium = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W400,
        fontSize = 45.sp,
        lineHeight = 52.sp,
        letterSpacing = 0.sp
    ),
    displaySmall = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W400,
        fontSize = 36.sp,
        lineHeight = 44.sp,
        letterSpacing = 0.sp
    ),
    headlineLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W400,
        fontSize = 32.sp,
        lineHeight = 40.sp,
        letterSpacing = 0.sp
    ),
    // ... more styles for headlineMedium, headlineSmall, titleLarge, titleMedium, titleSmall, bodyLarge, bodyMedium, bodySmall, labelLarge, labelMedium, labelSmall
)
Enter fullscreen mode Exit fullscreen mode

Shape System

The shape system in Material Design 3 uses a corner family system that allows you to define rounded corners consistently across your app.

val Shapes = Shapes(
    extraSmall = RoundedCornerShape(4.dp),
    small = RoundedCornerShape(8.dp),
    medium = RoundedCornerShape(12.dp),
    large = RoundedCornerShape(16.dp),
    extraLarge = RoundedCornerShape(28.dp)
)
Enter fullscreen mode Exit fullscreen mode

These shapes are used automatically by Material 3 components like buttons, cards, and dialog boxes, ensuring visual consistency throughout your application.

Using Material Theme Builder

The Material Theme Builder tool is an invaluable resource for creating custom Material Design 3 color schemes. Available at https://material-foundation.github.io/material-theme-builder/, this tool allows you to:

  1. Select a seed color - Choose a brand color that serves as the foundation for your entire color scheme
  2. Preview your theme - See how your colors look across various Material Design components
  3. Generate code - Export Kotlin or Compose code for your custom theme
  4. Adjust for accessibility - Ensure sufficient contrast ratios for text readability

Using the Material Theme Builder:

  1. Visit the web tool and select your primary brand color
  2. The tool automatically generates complementary primary, secondary, and tertiary colors
  3. Toggle between light and dark mode previews
  4. Copy the generated Kotlin code directly into your project
  5. Fine-tune colors if needed

Complete Theme Implementation Example

Here's a complete, production-ready theme implementation:

// Theme.kt
package com.example.myapp.ui.theme

import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext

// Color definitions
private val md_theme_light_primary = Color(0xFF006874)
private val md_theme_light_secondary = Color(0xFF4A6363)
private val md_theme_light_tertiary = Color(0xFF006B5E)

private val md_theme_dark_primary = Color(0xFF4FD8EB)
private val md_theme_dark_secondary = Color(0xFFB1CCCC)
private val md_theme_dark_tertiary = Color(0xFF52D9C4)

private val LightColorScheme = lightColorScheme(
    primary = md_theme_light_primary,
    secondary = md_theme_light_secondary,
    tertiary = md_theme_light_tertiary,
)

private val DarkColorScheme = darkColorScheme(
    primary = md_theme_dark_primary,
    secondary = md_theme_dark_secondary,
    tertiary = md_theme_dark_tertiary,
)

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }
        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}
Enter fullscreen mode Exit fullscreen mode

Best Practices for Material Design 3 Theming

1. Consistency is Key

Use Material Theme Builder to generate your entire color scheme from a seed color. This ensures mathematical consistency across all colors.

2. Test Accessibility

Always verify that your color combinations meet WCAG AA standards for contrast ratios. Material Theme Builder includes accessibility checks.

3. Respect User Preferences

Always support dark mode and enable dynamic color when targeting Android 12+. Users appreciate apps that respect their system settings.

4. Use Semantic Colors

Instead of hardcoding colors in components, use the semantic color names from your theme (primary, secondary, tertiary, error, etc.).

5. Container Variants for Subtle Hierarchy

Use the container color variants (primaryContainer, secondaryContainer, etc.) for backgrounds of less emphasized elements.

Conclusion

Material Design 3 theming in Jetpack Compose represents the modern standard for Android app design. By mastering the color system, typography, shapes, and dynamic color support, you can create visually stunning and accessible applications that users love to interact with.

The combination of Material Theme Builder for design and Jetpack Compose for implementation makes it easier than ever to create consistent, beautiful Android apps. Start with a seed color in Material Theme Builder, generate your code, and leverage Compose's declarative nature to build interfaces that truly shine.

All 8 templates use Material3. https://myougatheax.gumroad.com

Top comments (0)