Jetpack Compose has revolutionized Android development by offering a modern, declarative approach to building user interfaces. One of the most sought-after UI patterns in mobile app design is the sticky app bar, where a header or app bar remains at the top of the screen even as users scroll through content. This blog post explores how to create a sticky app bar in Jetpack Compose, diving into both the basics and advanced customization options.
What is a Sticky App Bar?
A sticky app bar, often referred to as a pinned header, is a UI component that stays fixed at the top of the screen while users scroll through other content. This feature is particularly useful for improving navigation and maintaining context in apps with long, scrollable content.
In Jetpack Compose, achieving this functionality involves understanding key components like LazyColumn
, Modifier
, and ScrollableState
.
Benefits of a Sticky App Bar
Improved Usability: Ensures navigation options or key information are always visible.
Enhanced Design: Adds a polished and professional look to your app.
Better Context: Keeps users aware of their current screen or section.
Step-by-Step Guide to Creating a Sticky App Bar
1. Setting Up the Project
Before diving into code, ensure your project is set up to use Jetpack Compose. Add the necessary dependencies in your build.gradle
file:
def compose_version = "1.5.0"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.foundation:foundation:$compose_version"
2. Basic Layout with LazyColumn
The LazyColumn
is the go-to component for implementing vertical scrolling lists in Jetpack Compose. It supports composable headers, making it perfect for creating a sticky app bar.
@Composable
fun StickyAppBarExample() {
LazyColumn {
stickyHeader {
AppBar("Sticky Header")
}
items(50) { index ->
ListItem(index)
}
}
}
@Composable
fun AppBar(title: String) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.primary)
.padding(16.dp)
) {
Text(
text = title,
color = Color.White,
style = MaterialTheme.typography.h6
)
}
}
@Composable
fun ListItem(index: Int) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(text = "Item #$index")
}
}
Key Takeaways:
stickyHeader {}
: A special function inLazyColumn
that ensures the composable remains fixed at the top.Performance:
LazyColumn
optimizes performance by lazily rendering items as they appear on the screen.
Advanced Customizations
While the basic implementation works, real-world apps often require additional features like animations, dynamic styling, and integrating multiple sticky headers. Here’s how you can enhance your sticky app bar.
1. Adding Animations
To make your sticky app bar visually engaging, you can add animations that respond to scroll state. For instance, shrinking the app bar as the user scrolls:
@Composable
fun AnimatedStickyAppBar() {
val scrollState = rememberLazyListState()
val appBarHeight = (56 - (scrollState.firstVisibleItemScrollOffset / 10)).coerceIn(40, 56)
LazyColumn(state = scrollState) {
stickyHeader {
Box(
modifier = Modifier
.fillMaxWidth()
.height(appBarHeight.dp)
.background(MaterialTheme.colors.primary)
.padding(16.dp)
) {
Text(
text = "Animated Sticky Header",
color = Color.White,
style = MaterialTheme.typography.h6
)
}
}
items(50) { index ->
ListItem(index)
}
}
}
Key Concepts:
LazyListState
: Captures the scroll state of the list.Dynamic Heights: Use
coerceIn
to maintain reasonable height limits for the app bar.
2. Multiple Sticky Headers
For apps requiring multiple sections with sticky headers:
@Composable
fun MultiSectionList() {
val sections = listOf("Section 1" to 10, "Section 2" to 20, "Section 3" to 15)
LazyColumn {
sections.forEach { (section, itemCount) ->
stickyHeader {
AppBar(section)
}
items(itemCount) { index ->
ListItem(index)
}
}
}
}
This approach groups items under specific headers, keeping them sticky as you scroll.
3. Custom Scroll Behaviors
Using NestedScrollConnection
, you can create highly customized scroll behaviors, such as parallax effects or synchronized scrolling between multiple components:
val scrollBehavior = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// Implement custom scroll logic
return Offset.Zero
}
}
}
Box(
modifier = Modifier.nestedScroll(scrollBehavior)
) {
// Your scrollable content
}
Best Practices
1. Optimize Performance
Use
LazyColumn
for lists instead ofColumn
to avoid performance issues.Keep sticky headers lightweight to minimize rendering overhead.
2. Accessibility
Add content descriptions to ensure your sticky app bar is accessible to screen readers.
Text(
text = title,
color = Color.White,
style = MaterialTheme.typography.h6,
modifier = Modifier.semantics { contentDescription = "Sticky Header: $title" }
)
3. Consistency with Material Design
Leverage Material Design components for a polished look.
Use
MaterialTheme
for colors, typography, and shapes.
Conclusion
Creating a sticky app bar in Jetpack Compose is straightforward yet powerful. By leveraging LazyColumn
and its stickyHeader
capability, you can implement robust and visually appealing UI patterns. With advanced customizations like animations, multiple sticky headers, and custom scroll behaviors, your app can stand out in terms of both design and functionality.
Jetpack Compose continues to empower developers with its declarative approach, and mastering concepts like sticky app bars can significantly elevate your app’s user experience.
Have you implemented sticky app bars in your projects? Share your experiences and tips in the comments below!