This tutorial will teach us, How to implement KMP light and dark theme modes with Compose multiplatform for Android and iOS devices. We will be explaining 2 approaches for setting up dark and light themes, The First is to automatically detect the system theme and update the device theme. The second is to update the app theme manually. So let’s get started.
KMP Light and Dark Theme Modes with Compose Multiplatform
1. Automatically detect the Android iOS device theme and update the KMP(KMM) app theme.
In Compose Multiplatform when we start development then all the widgets and components are used in the Child and Children format. Every component has its Root component. But the main Root component we always define for our app is the MaterialTheme composable component. This component has property colors, Which work as app theme colors and we can trigger these colors on App dark and light theme modes separately.
1. Open your KMP compose multiplatform project’s App.kt file to start coding.
2. Define colors in Hex format for your application light mode theme. We can define all the colors here that we want to use in our app when the app is in Light theme mode.
1 2 3 4 5 |
private val LightColorPalette = lightColors( primary = Color(0xFF6200EE), secondary = Color(0xFF03DAC5), background = Color.White, ) |
3. Define dark mode theme colors.
1 2 3 4 5 |
private val DarkColorPalette = darkColors( primary = Color(0xFFBB86FC), secondary = Color(0xFF03DAC5), background = Color.Black, ) |
4. Defining a composable component named as MyCustomTheme. In this widget, we will implement the isSystemInDarkTheme() inbuilt method which returns the current device theme. This is our app Root widget.
1 2 3 4 5 6 7 8 9 |
@Composable fun MyCustomTheme(content: @Composable () -> Unit) { val colors = if (isSystemInDarkTheme()) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, content = content ) } |
5. Defining WelcomeScreen widget. This is our main Child wiget in which our whole View is defined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@Composable fun WelcomeScreen() { Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "Hello, Kotlin Guide, Dark Mode Theme & Light Mode Theme!", color = MaterialTheme.colors.onBackground, style = MaterialTheme.typography.h5, textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(16.dp)) Button( onClick = {}, colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.primary, contentColor = MaterialTheme.colors.onPrimary ) ) { Text("Sample Button") } } } |
6. Defining our Main App widget and here we will call our all child view.
1 2 3 4 5 6 7 8 9 10 11 |
@Composable fun App() { MyCustomTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { WelcomeScreen() } } } |
Complete Source Code for App.kt file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
package com.app.test import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.darkColors import androidx.compose.material.lightColors import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp private val LightColorPalette = lightColors( primary = Color(0xFF6200EE), secondary = Color(0xFF03DAC5), background = Color.White, ) private val DarkColorPalette = darkColors( primary = Color(0xFFBB86FC), secondary = Color(0xFF03DAC5), background = Color.Black, ) @Composable fun MyCustomTheme(content: @Composable () -> Unit) { val colors = if (isSystemInDarkTheme()) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, content = content ) } @Composable fun App() { MyCustomTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { WelcomeScreen() } } } @Composable fun WelcomeScreen() { Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "Hello, Kotlin Guide, Dark Mode Theme & Light Mode Theme!", color = MaterialTheme.colors.onBackground, style = MaterialTheme.typography.h5, textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(16.dp)) Button( onClick = {}, colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.primary, contentColor = MaterialTheme.colors.onPrimary ) ) { Text("Sample Button") } } } |
App Screenshots:
2. Automatically detect & Manually Change/Switch the KMP App theme.
In this code, we are managing the app theme using a State. First, we detect the device theme then we change its value using Mutable State on the button click event.
Complete Source code for App.kt file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
package com.app.test import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp private val LightColorPalette = lightColors( primary = Color(0xFF6200EE), secondary = Color(0xFF03DAC5), background = Color.White, ) private val DarkColorPalette = darkColors( primary = Color(0xFFBB86FC), secondary = Color(0xFF03DAC5), background = Color.Black, ) @Composable fun MyCustomTheme( isDarkTheme: Boolean, content: @Composable () -> Unit ) { val colors = if (isDarkTheme) DarkColorPalette else LightColorPalette MaterialTheme( colors = colors, content = content ) } @Composable fun App() { val systemInDarkTheme = isSystemInDarkTheme() // Mutable state to track theme mode var isDarkTheme by remember { mutableStateOf(systemInDarkTheme) } MyCustomTheme(isDarkTheme = isDarkTheme) { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { WelcomeScreen( isDarkTheme = isDarkTheme, onThemeToggle = { isDarkTheme = !isDarkTheme } // Toggle theme ) } } } @Composable fun WelcomeScreen( isDarkTheme: Boolean, onThemeToggle: () -> Unit ) { Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "Hello, Kotlin Guide, Dark Mode Theme & Light Mode Theme!", color = MaterialTheme.colors.onBackground, style = MaterialTheme.typography.h5, textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(16.dp)) Button( onClick = onThemeToggle, // Toggle the theme when clicked colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.primary, contentColor = MaterialTheme.colors.onPrimary ) ) { Text( text = if (isDarkTheme) "Switch to Light Mode" else "Switch to Dark Mode" ) } } } |
Screenshots: