Dark theme has become an essential feature in modern mobile applications, providing a comfortable viewing experience in low-light environments while potentially conserving battery life. Jetpack Compose makes it straightforward to implement dark theme support in your Android apps. This post will guide you through the process of integrating dark theme functionality using Jetpack Compose.
What is Dark Theme?
Dark theme is a color scheme that uses dark colors for the primary surface of the UI, reducing the light emitted by the screen. It’s beneficial for users in dark environments and can help save battery on devices with OLED screens.
Why Implement Dark Theme?
- Improved User Experience: Reduces eye strain in low-light conditions.
- Battery Saving: Conserves battery on OLED screens by using less power to display dark pixels.
- Accessibility: Enhances readability for users with visual impairments.
How to Implement Dark Theme in Jetpack Compose
Step 1: Set Up Dependencies
Ensure you have the necessary dependencies in your build.gradle
file. Compose Material and UI dependencies are required:
dependencies {
implementation("androidx.compose.ui:ui:1.6.1")
implementation("androidx.compose.material:material:1.6.1")
implementation("androidx.compose.ui:ui-tooling-preview:1.6.1")
debugImplementation("androidx.compose.ui:ui-tooling:1.6.1")
}
Step 2: Define Color Schemes
Create color schemes for both light and dark themes. Compose provides the ColorScheme
class to manage theme colors.
import androidx.compose.ui.graphics.Color
import androidx.compose.material.lightColorScheme
import androidx.compose.material.darkColorScheme
val LightColorScheme = lightColorScheme(
primary = Color(0xFF6200EE),
secondary = Color(0xFF03DAC5),
background = Color.White,
surface = Color.White,
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
)
val DarkColorScheme = darkColorScheme(
primary = Color(0xFFBB86FC),
secondary = Color(0xFF03DAC5),
background = Color(0xFF121212),
surface = Color(0xFF121212),
onPrimary = Color.Black,
onSecondary = Color.Black,
onBackground = Color.White,
onSurface = Color.White,
)
Step 3: Create a Theme Composable
Create a theme composable that wraps your UI and provides the appropriate color scheme based on the system’s dark theme setting.
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
@Composable
fun AppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
val colors = if (darkTheme) {
DarkColorScheme
} else {
LightColorScheme
}
MaterialTheme(
colors = colors,
content = content
)
}
Explanation:
- `isSystemInDarkTheme()`: This function from the `androidx.compose.foundation` package checks if the system is currently in dark mode.
- Color Scheme Selection: Based on the system’s dark theme setting, the composable selects either the `DarkColorScheme` or `LightColorScheme`.
- MaterialTheme: Wraps the provided content with the selected color scheme, ensuring that all composables within this scope use the correct colors.
Step 4: Apply the Theme to Your App
Wrap your main composable with the AppTheme
composable.
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.Text
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.material.Button
import androidx.compose.runtime.*
import androidx.compose.ui.tooling.preview.Preview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
MyAppContent()
}
}
}
}
@Composable
fun MyAppContent() {
var count by remember { mutableStateOf(0) }
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Count: $count")
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
AppTheme {
MyAppContent()
}
}
In this example:
MainActivity
sets the content toAppTheme
wrapping theMyAppContent
composable.MyAppContent
is a simple composable that displays a counter and a button.
Step 5: Dynamic Theme Switching
To allow users to manually switch between light and dark themes within the app, you can use remember
and mutableStateOf
to manage the theme state.
import androidx.compose.runtime.*
import androidx.compose.material.*
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun AppTheme(
isDarkThemeEnabled: MutableState<Boolean> = remember { mutableStateOf(false) },
content: @Composable () -> Unit
) {
val colors = if (isDarkThemeEnabled.value) {
DarkColorScheme
} else {
LightColorScheme
}
MaterialTheme(
colors = colors,
content = content
)
}
@Composable
fun MyApp() {
var isDarkThemeEnabled = remember { mutableStateOf(false) }
AppTheme(isDarkThemeEnabled) {
Surface(color = MaterialTheme.colors.background) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Dark Theme Demo", style = MaterialTheme.typography.h5)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { isDarkThemeEnabled.value = !isDarkThemeEnabled.value }) {
Text(text = if (isDarkThemeEnabled.value) "Disable Dark Theme" else "Enable Dark Theme")
}
}
}
}
}
Explanation:
- `isDarkThemeEnabled` State: A
mutableStateOf
is used to hold the current theme state. - Theme Toggling Button: A button toggles the `isDarkThemeEnabled` state, updating the theme dynamically.
- `AppTheme` Update: The
AppTheme
composable reads the current state and applies the appropriate color scheme.
Conclusion
Implementing dark theme in Jetpack Compose is straightforward and enhances user experience and accessibility. By defining color schemes, creating a theme composable, and applying it to your app, you can easily support dark theme. The dynamic theme switching provides users with the flexibility to choose their preferred viewing mode. By following these steps, your app can deliver a more comfortable and energy-efficient experience, regardless of the ambient lighting.