Jetpack Compose has revolutionized Android UI development by providing a declarative and reactive way to build user interfaces. One of the most exciting features is its support for dynamic colors, which allows your app to adapt its color scheme based on various factors, such as user preferences or the system theme. Using dynamic colors, you can provide a personalized and cohesive user experience across different devices and themes.
What are Dynamic Colors?
Dynamic colors are color schemes that change based on contextual inputs, such as the user’s wallpaper or system theme. This allows apps to feel more integrated with the device’s overall aesthetic, enhancing user satisfaction and visual appeal. Dynamic colors are particularly useful in Android 12 and above, where the Material You design system leverages them extensively.
Why Use Dynamic Colors?
- Enhanced User Experience: Provides a cohesive and personalized look by adapting to the user’s theme.
- Accessibility: Improves readability and accessibility by adjusting colors to ensure sufficient contrast.
- Modern Design: Aligns with the Material You design principles, offering a fresh and modern UI.
How to Implement Dynamic Colors in Jetpack Compose
Implementing dynamic colors in Jetpack Compose involves utilizing the MaterialTheme and the dynamicLightColorScheme and dynamicDarkColorScheme functions.
Step 1: Add Material 3 Dependency
First, ensure you have the Material 3 dependency in your build.gradle file:
dependencies {
implementation("androidx.compose.material3:material3:1.1.2") // Use the latest version
}
Step 2: Create a Compose Theme with Dynamic Colors
Define a custom Compose theme that uses dynamic color schemes:
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
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
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
In this code:
- We check if dynamic color support is available (Android 12 and above).
- If dynamic color is supported, we use
dynamicLightColorSchemeordynamicDarkColorSchemebased on the current theme. - If dynamic color is not supported, we fall back to predefined light or dark color schemes.
- The
MaterialThemecomposable applies the chosen color scheme to its content.
Step 3: Use the Custom Theme in Your App
Wrap your composable content with the custom theme:
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
@Composable
fun MyScreenContent() {
Text(text = "Hello, Dynamic Colors!")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
MyAppTheme {
MyScreenContent()
}
}
Now, run your app on an Android 12+ device, and you should see the colors adapt based on your device’s theme or wallpaper.
Advanced Customization
For further customization, you can also define your own color schemes and map dynamic colors to your custom colors.
Example of Custom Color Mapping
import androidx.compose.ui.graphics.Color
// Custom colors
val customPrimaryColor = Color(0xFF6200EE)
val customSecondaryColor = Color(0xFF03DAC5)
private val DarkColorScheme = darkColorScheme(
primary = customPrimaryColor,
secondary = customSecondaryColor,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = customPrimaryColor,
secondary = customSecondaryColor,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
Handling Different Android Versions
Since dynamic colors are available from Android 12 (API level 31), you may need to handle older versions differently to maintain a consistent UI.
import android.os.Build
import androidx.compose.runtime.Composable
@Composable
fun MyThemedContent(content: @Composable () -> Unit) {
val useDynamicColors = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
MyAppTheme(dynamicColor = useDynamicColors) {
content()
}
}
By using the above snippet, you ensure that dynamic colors are used only on devices that support them, falling back to your default color scheme on older devices.
Conclusion
Dynamic colors in Jetpack Compose offer a seamless way to create visually appealing and user-centric applications that adapt to the user’s device settings. By incorporating dynamic colors, you not only provide a modern user experience but also improve the overall accessibility and integration of your app within the Android ecosystem. Utilizing dynamicLightColorScheme and dynamicDarkColorScheme ensures that your application aligns with Material You’s design principles, creating a cohesive and delightful user interface.