Adding Dark Mode Support in Android – Theme Switching with Jetpack Compose

📰 Introduction

Dark Mode has become a standard feature in modern Android apps. It enhances visual comfort, saves battery on AMOLED screens, and aligns with the user’s system preferences. With Jetpack Compose, implementing dark and light themes becomes clean, modular, and developer-friendly.

In this tutorial, you’ll learn:

  • 🔧 How to create light and dark themes in Jetpack Compose
  • 🔁 How to toggle between themes using a switch
  • 💾 How to save theme preference using Jetpack DataStore
  • 🎨 Best practices for Dark Mode design
  • ✅ Kotlin code examples for real implementation
Infographic outlining the steps to set up dark/light themes, apply MaterialTheme, and manage dynamic theme switching in Jetpack Compose.

🎨 Create Light and Dark Themes

Define color schemes in your Theme.kt file:

kotlinCopyEditprivate val LightColors = lightColorScheme(
    primary = Blue500,
    onPrimary = White,
    background = White,
    onBackground = Black,
    surface = White,
    onSurface = Black
)

private val DarkColors = darkColorScheme(
    primary = Blue200,
    onPrimary = Black,
    background = DarkGray,
    onBackground = White,
    surface = DarkGray,
    onSurface = White
)

⚙️ Apply Theme Using MaterialTheme

Use Compose’s MaterialTheme to wrap your UI:

kotlinCopyEdit@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) DarkColors else LightColors

    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        content = content
    )
}

💡 isSystemInDarkTheme() respects the user’s device settings automatically.


🔁 Toggle Dark Mode with a Switch

kotlinCopyEdit@Composable
fun DarkModeToggle(isDark: Boolean, onToggle: () -> Unit) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Text(text = "Dark Mode")
        Switch(checked = isDark, onCheckedChange = { onToggle() })
    }
}

💾 Persist User Preference with DataStore

Use Jetpack DataStore to save user settings.

1. Setup DataStore

kotlinCopyEditval Context.dataStore by preferencesDataStore("user_prefs")
val DARK_MODE_KEY = booleanPreferencesKey("dark_mode_enabled")

2. Save & Retrieve

kotlinCopyEditsuspend fun saveThemePreference(context: Context, isDark: Boolean) {
    context.dataStore.edit { settings ->
        settings[DARK_MODE_KEY] = isDark
    }
}

fun readThemePreference(context: Context): Flow<Boolean> =
    context.dataStore.data.map { prefs -> prefs[DARK_MODE_KEY] ?: false }

🧠 ViewModel Integration

kotlinCopyEditclass ThemeViewModel(context: Context) : ViewModel() {
    private val _isDarkMode = MutableStateFlow(false)
    val isDarkMode: StateFlow<Boolean> = _isDarkMode

    init {
        viewModelScope.launch {
            readThemePreference(context).collect {
                _isDarkMode.value = it
            }
        }
    }

    fun toggleTheme(context: Context) {
        viewModelScope.launch {
            saveThemePreference(context, !_isDarkMode.value)
        }
    }
}

Code diagram displaying Jetpack Compose theme structure with lightTheme, darkTheme, and system-based UI mode toggle logic.

🧩 Best Practices for Dark Mode in Android

✅ Tip🔍 Description
Use isSystemInDarkTheme()Respect system preferences for accessibility
Avoid pure black/whiteUse #121212 or Material Design colors
Test your UI in both modesCatch layout or contrast issues
Use adaptive icons and imagesProvide versions suitable for dark backgrounds
Stick to Material You guidelinesHelps maintain visual consistency

Key Points – Dark Mode in Jetpack Compose

  1. Importance of Dark Mode
    • Enhances visual ergonomics and battery life
    • Improves accessibility for light-sensitive users
  2. Jetpack Compose Theming Basics
    • Uses MaterialTheme, colors, shapes, and typography
    • Separates light and dark color palettes
  3. Detect System Dark Mode
    • Use isSystemInDarkTheme() to auto-detect system preference
    • Enables app to follow the device-wide theme setting
  4. Manually Toggle Dark Mode
    • Implement user-controlled toggle via Switch or Button
    • Provide override option for users who want manual control
  5. Store Theme Preference with DataStore
    • Persist theme preference locally using DataStore
    • Retrieve and apply theme on app startup
  6. ViewModel for Theme Management
    • Use ViewModel to expose theme state via StateFlow
    • Keeps UI reactive and lifecycle-aware
  7. Composable Previews for Light & Dark Modes
    • Use @Preview annotations to check both themes during development
    • Improves design consistency
  8. Avoid Hardcoded Colors
    • Always use MaterialTheme.colors
    • Ensures consistent styling in both modes
  9. Test UI in Both Modes
    • Validate contrast, visibility, and brand colors in dark/light modes
    • Ensure components adapt properly
  10. SEO Tip for Developers
    • Add theme support keywords like Jetpack Compose Dark Mode, Theme switching, MaterialTheme, etc., in content and image ALT tags for better visibility

🔗 Internal Links for Contextual Learning


❓ Frequently Asked Questions

How do I enable dark mode in Jetpack Compose?

Use the MaterialTheme and isSystemInDarkTheme() functions to automatically apply dark or light themes based on system settings or user preferences.

How do I persist dark mode settings in Android?

You can use Jetpack DataStore to save the user’s dark mode preference across app launches.

How do I store dark mode preference in Android?

Use Jetpack DataStore to persist dark mode preferences using a booleanPreferencesKey and flow-based API for reading the saved values.

Does dark mode save battery?

Yes, on OLED displays, dark mode reduces power consumption by turning off unused pixels, improving battery life.

🧾 Conclusion

Adding Dark Mode support in Android using Jetpack Compose is no longer a luxury—it’s a user expectation. With Jetpack Compose’s declarative design, theme switching becomes easier, cleaner, and more scalable than ever before. By leveraging MaterialTheme, isSystemInDarkTheme(), and Jetpack DataStore, you can provide users with a seamless experience that respects system settings and personal preferences.

From creating light/dark color schemes to implementing a toggle switch and persisting state using ViewModel + DataStore, this guide covered a complete, production-ready solution.

Whether you’re building a small app or a large-scale product, Dark Mode support improves user satisfaction, accessibility, and energy efficiency.

Start implementing it today and make your Android apps visually adaptive, polished, and modern.

Leave a Comment

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

Scroll to Top