Customizing System Bars in Jetpack Compose with Material You

In modern Android app development, providing a seamless and aesthetically pleasing user experience is paramount. System bars – the status bar and navigation bar – play a critical role in this experience. With Android’s Material You design and Jetpack Compose, developers have more power than ever to customize these elements dynamically to match the app’s theme and user preferences.

This post delves into advanced techniques for customizing system bars using Jetpack Compose and Material You, focusing on best practices, practical examples, and advanced use cases.

Understanding Material You and System Bars

Material You is Google’s latest design language introduced in Android 12. It emphasizes dynamic color extraction and personalization, enabling apps to adapt their UI to the user’s chosen wallpaper or theme.

System bars include:

  • Status Bar: Displays notifications, time, and system icons.

  • Navigation Bar: Houses navigation gestures or virtual buttons.

Customizing these bars helps blend the app’s UI seamlessly with Material You’s dynamic theming.

Setting Up Dynamic Themes with Material You

Jetpack Compose simplifies implementing Material You themes with its MaterialTheme composable. Here’s how to set up dynamic theming:

Step 1: Enable Dynamic Colors

Ensure your app targets Android 12 or higher and includes the androidx.compose.material3:material3 library.

// build.gradle (app level)
dependencies {
    implementation "androidx.compose.material3:material3:1.x.x"
}

Step 2: Create a Theme Function

@Composable
fun MyAppTheme(content: @Composable () -> Unit) {
    val dynamicColors = MaterialTheme.colorScheme

    MaterialTheme(
        colorScheme = dynamicColors,
        typography = Typography,
        content = content
    )
}

Step 3: Apply the Theme

Wrap your app’s navigation graph or main composable with MyAppTheme to ensure dynamic colors are applied universally.

@Composable
fun MyApp() {
    MyAppTheme {
        NavigationGraph()
    }
}

Customizing the Status Bar

The status bar’s color and icon visibility can be controlled in Compose using the SystemUiController from the accompanist-systemuicontroller library.

Step 1: Add the Library

// build.gradle (app level)
dependencies {
    implementation "com.google.accompanist:accompanist-systemuicontroller:0.x.x"
}

Step 2: Update the Status Bar Color

Use SystemUiController to dynamically set the status bar color and icon visibility.

@Composable
fun CustomStatusBar() {
    val systemUiController = rememberSystemUiController()
    val useDarkIcons = MaterialTheme.colorScheme.isLight
    val statusBarColor = MaterialTheme.colorScheme.primary

    LaunchedEffect(systemUiController, statusBarColor) {
        systemUiController.setStatusBarColor(
            color = statusBarColor,
            darkIcons = useDarkIcons
        )
    }
}

Place this composable inside your app’s main UI to ensure the status bar matches the theme.

Best Practice: Transparency

To make the status bar transparent and allow the app content to extend beneath it:

systemUiController.setStatusBarColor(
    color = Color.Transparent,
    darkIcons = useDarkIcons
)

Customizing the Navigation Bar

The navigation bar can be styled similarly, but with some nuances.

Step 1: Update the Navigation Bar Color

@Composable
fun CustomNavigationBar() {
    val systemUiController = rememberSystemUiController()
    val navigationBarColor = MaterialTheme.colorScheme.surface

    LaunchedEffect(systemUiController, navigationBarColor) {
        systemUiController.setNavigationBarColor(
            color = navigationBarColor
        )
    }
}

Step 2: Handling Gesture Navigation

For devices using gesture navigation, avoid bright colors that clash with the gestures' indicators.

val safeNavigationBarColor = if (isGestureNavigationEnabled()) {
    Color.Transparent
} else {
    MaterialTheme.colorScheme.surface
}

Best Practice: Contrast

Ensure sufficient contrast between the navigation bar color and icons for accessibility.

Integrating with Material You’s Dynamic Colors

Material You automatically generates a color palette based on the user’s wallpaper. Leverage these dynamic colors to create a cohesive system bar theme:

Example: Dynamic Status and Navigation Bar

@Composable
fun DynamicSystemBars() {
    val systemUiController = rememberSystemUiController()
    val colors = MaterialTheme.colorScheme

    LaunchedEffect(systemUiController) {
        systemUiController.setStatusBarColor(
            color = colors.primaryContainer,
            darkIcons = colors.isLight
        )
        systemUiController.setNavigationBarColor(
            color = colors.surface
        )
    }
}

Testing System Bar Customization

To ensure your customizations work across devices:

  1. Test on Multiple API Levels: Validate functionality on Android 12+ for Material You features.

  2. Check Light and Dark Themes: Switch themes to confirm proper icon visibility and contrast.

  3. Handle Edge Cases: Test with different wallpapers and gesture/navigation modes.

Advanced Use Cases

Animating System Bar Colors

Dynamically animate system bar colors during screen transitions or user interactions using animateColorAsState:

val animatedColor by animateColorAsState(targetValue = targetColor)

LaunchedEffect(systemUiController) {
    systemUiController.setStatusBarColor(animatedColor)
}

Per-Screen Customization

Customize system bars for individual screens by updating colors in each screen’s Composable function.

@Composable
fun ProfileScreen() {
    val systemUiController = rememberSystemUiController()

    LaunchedEffect(systemUiController) {
        systemUiController.setStatusBarColor(MaterialTheme.colorScheme.primary)
    }

    // Screen content...
}

Conclusion

Customizing system bars in Jetpack Compose with Material You allows developers to create polished, immersive experiences that align with modern design trends. By leveraging dynamic colors, the SystemUiController, and best practices, you can ensure your app’s UI stands out while maintaining consistency with user preferences.

Start implementing these techniques today and give your app the edge it deserves!