Create a Collapsing App Bar in Jetpack Compose

Jetpack Compose is revolutionizing the way Android developers create modern and reactive user interfaces. One of the most common UI patterns in Android apps is the Collapsing App Bar, which provides an engaging and intuitive navigation experience. In this blog post, we’ll explore how to implement a Collapsing App Bar using Jetpack Compose, delve into best practices, and optimize the design for performance and user experience.

Why Use a Collapsing App Bar?

The Collapsing App Bar, often seen in apps like YouTube and Gmail, enhances user experience by dynamically resizing as users scroll through content. This pattern:

  • Improves visual hierarchy by emphasizing the main content.

  • Saves screen space by collapsing headers into smaller toolbars.

  • Provides a modern, polished look and feel to your app.

Jetpack Compose simplifies the implementation of this pattern with its declarative approach to UI development. Let’s dive into how you can build one.

Prerequisites

Before proceeding, ensure you’re familiar with the following:

  1. Basic understanding of Jetpack Compose.

  2. Familiarity with state management in Compose.

  3. Knowledge of the LazyColumn or LazyList for scrollable content.

You’ll also need the latest version of Android Studio with Compose support enabled.

Key Components of a Collapsing App Bar

To create a Collapsing App Bar in Jetpack Compose, we’ll use the following components:

  1. TopAppBar: Displays the title and optional actions.

  2. Modifier: Handles scrolling effects and dynamic height adjustments.

  3. LazyColumn: Provides scrollable content beneath the App Bar.

  4. NestedScrollConnection: Coordinates the collapsing behavior.

Implementation: Step-by-Step Guide

Step 1: Set Up the Project

Start by creating a new Jetpack Compose project in Android Studio. Add the following dependencies to your build.gradle file:

implementation "androidx.compose.material:material:1.x.x"
implementation "androidx.compose.ui:ui:1.x.x"
implementation "androidx.compose.runtime:runtime:1.x.x"
implementation "androidx.compose.foundation:foundation:1.x.x"

Sync your project to download the required libraries.

Step 2: Create the Collapsing App Bar Layout

Here’s the foundational layout for a collapsing App Bar:

@Composable
fun CollapsingAppBarExample() {
    val toolbarHeight = 200.dp
    val minToolbarHeight = 56.dp
    val scrollState = rememberScrollState()

    val toolbarOffsetHeightPx = with(LocalDensity.current) {
        maxOf(toolbarHeight.roundToPx() - scrollState.value, minToolbarHeight.roundToPx()).toFloat()
    }

    Box(Modifier.fillMaxSize()) {
        // Scrollable content
        LazyColumn(modifier = Modifier.fillMaxSize().padding(top = minToolbarHeight)) {
            items(50) { index ->
                Text(
                    text = "Item $index",
                    modifier = Modifier.fillMaxWidth().padding(16.dp)
                )
            }
        }

        // Collapsing App Bar
        TopAppBar(
            modifier = Modifier
                .height(with(LocalDensity.current) { toolbarOffsetHeightPx.toDp() })
                .align(Alignment.TopCenter),
            title = { Text("Collapsing App Bar") },
            backgroundColor = MaterialTheme.colors.primary
        )
    }
}

Step 3: Add Nested Scroll Behavior

To make the App Bar react smoothly to scrolling, integrate NestedScrollConnection. This allows the App Bar’s height to dynamically adjust as the user scrolls.

@Composable
fun CollapsingAppBarWithScroll() {
    val toolbarHeight = 200.dp
    val minToolbarHeight = 56.dp
    val scrollBehavior = remember { TopAppBarScrollBehavior() }

    Box(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
        // Scrollable content
        LazyColumn(modifier = Modifier.fillMaxSize()) {
            items(50) { index ->
                Text(
                    text = "Item $index",
                    modifier = Modifier.fillMaxWidth().padding(16.dp)
                )
            }
        }

        // Collapsing App Bar
        TopAppBar(
            title = { Text("Dynamic App Bar") },
            modifier = Modifier
                .height(scrollBehavior.height)
                .background(MaterialTheme.colors.primary)
        )
    }
}

@Stable
class TopAppBarScrollBehavior {
    var height: Dp by mutableStateOf(200.dp)

    val nestedScrollConnection = object : NestedScrollConnection {
        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
            height = (height - available.y.toDp()).coerceIn(56.dp, 200.dp)
            return Offset.Zero
        }
    }
}

Step 4: Optimize Performance

Use Compose’s Remember Functions

Use remember to minimize unnecessary recompositions. For instance, cache the scroll state and other heavy computations.

val scrollState = rememberLazyListState()

Use Lightweight Composables

Avoid using heavy composables inside LazyColumn or frequently updated UI components to improve performance.

Monitor Frame Rates

Use tools like Android Studio’s Layout Inspector and Profilers to ensure smooth animations and transitions.

Step 5: Add Custom Animations (Optional)

To make the collapsing effect more visually appealing, you can use Compose’s animation APIs:

val animatedHeight by animateDpAsState(targetValue = toolbarHeight)

TopAppBar(
    modifier = Modifier.height(animatedHeight),
    title = { Text("Collapsing App Bar") }
)

Best Practices for a Collapsing App Bar

  1. Test on Different Screen Sizes: Ensure your App Bar behaves consistently across various devices.

  2. Use Content Padding: Avoid content overlapping by adding padding dynamically based on the App Bar’s height.

  3. Avoid Complex Logic in Composables: Keep the UI logic separate from business logic for better maintainability.

Conclusion

Creating a Collapsing App Bar in Jetpack Compose combines the power of declarative UI with the flexibility of Compose’s animation and scroll handling APIs. By following the steps and best practices outlined in this post, you can implement a visually engaging and performant Collapsing App Bar for your Android app.

Jetpack Compose continues to evolve, and mastering patterns like this ensures your apps stay modern and responsive. Try this implementation in your next project and unlock new possibilities with Jetpack Compose!