TopAppBar in Compose: All 4 Variants & Scroll Behavior Guide
Jetpack Compose Material3 offers four TopAppBar variants with flexible scroll behaviors. Let's master them all.
1. Small TopAppBar (Default)
The most compact variant, perfect for basic app bars.
@Composable
fun SmallTopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = TopAppBarDefaults.smallTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null,
) {}
Example:
TopAppBar(
title = { Text("My App") },
navigationIcon = {
IconButton(onClick = { /* Menu */ }) {
Icon(Icons.Default.Menu, contentDescription = null)
}
},
actions = {
IconButton(onClick = { /* Search */ }) {
Icon(Icons.Default.Search, contentDescription = null)
}
IconButton(onClick = { /* More */ }) {
Icon(Icons.Default.MoreVert, contentDescription = null)
}
}
)
2. CenterAligned TopAppBar
Title is centered, useful for detail screens.
@Composable
fun CenterAlignedTopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = TopAppBarDefaults.centerAlignedTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null,
) {}
Example:
CenterAlignedTopAppBar(
title = { Text("Article Details") },
navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) {
Icon(Icons.Default.ArrowBack, contentDescription = null)
}
}
)
3. Medium TopAppBar
Larger title with more breathing room. Collapses on scroll.
MediumTopAppBar(
title = { Text("Headlines", modifier = Modifier.padding(12.dp)) },
navigationIcon = {
IconButton(onClick = { /* Menu */ }) {
Icon(Icons.Default.Menu, contentDescription = null)
}
},
scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
)
4. Large TopAppBar
Maximum height variant for prominent titles. Great for search or category screens.
LargeTopAppBar(
title = { Text("Explore", modifier = Modifier.padding(12.dp)) },
navigationIcon = {
IconButton(onClick = { /* Menu */ }) {
Icon(Icons.Default.Menu, contentDescription = null)
}
},
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
)
Scroll Behaviors
enterAlways
App bar always appears at the top on scroll up.
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState())
Scaffold(
topBar = {
SmallTopAppBar(
title = { Text("Items") },
scrollBehavior = scrollBehavior
)
},
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { paddingValues ->
LazyColumn(
contentPadding = paddingValues,
modifier = Modifier.fillMaxSize()
) {
items(100) { index ->
ListItem(headlineContent = { Text("Item $index") })
}
}
}
exitUntilCollapsed
App bar collapses fully before list scrolls.
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
rememberTopAppBarState()
)
Scaffold(
topBar = {
LargeTopAppBar(
title = { Text("Categories") },
scrollBehavior = scrollBehavior
)
},
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { paddingValues ->
// List content
}
pinned
App bar stays fixed at the top (no scroll collapse).
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold(
topBar = {
SmallTopAppBar(
title = { Text("Fixed Header") },
scrollBehavior = scrollBehavior
)
},
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { paddingValues ->
// Content scrolls behind fixed app bar
}
Custom Colors
val customColors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = Color.Blue,
titleContentColor = Color.White,
navigationIconContentColor = Color.White,
actionIconContentColor = Color.White,
scrolledContainerColor = Color.DarkGray
)
TopAppBar(
title = { Text("Styled App") },
colors = customColors,
navigationIcon = { /* ... */ },
actions = { /* ... */ }
)
Complete Example with NestedScroll
@Composable
fun CompleteAppBarExample() {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
rememberTopAppBarState()
)
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
LargeTopAppBar(
title = {
Text(
"My Articles",
modifier = Modifier
.padding(12.dp)
.fillMaxWidth(),
style = MaterialTheme.typography.headlineSmall
)
},
navigationIcon = {
IconButton(onClick = { /* Menu */ }) {
Icon(Icons.Default.Menu, contentDescription = null)
}
},
actions = {
IconButton(onClick = { /* Search */ }) {
Icon(Icons.Default.Search, contentDescription = null)
}
},
scrollBehavior = scrollBehavior
)
}
) { paddingValues ->
LazyColumn(
contentPadding = paddingValues,
modifier = Modifier.fillMaxSize()
) {
items(50) { index ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
) {
ListItem(
headlineContent = { Text("Article $index") },
supportingContent = { Text("Author: John Doe") }
)
}
}
}
}
}
Best Practices
- Use Small for most screens - Default choice, minimal overhead
- Use CenterAligned for detail pages - Provides focus
- Use Medium/Large for exploratory screens - Better visual hierarchy
- Always pair with nestedScroll - Required for scroll behaviors to work
-
Choose scroll behavior wisely:
- enterAlways - Quick access to app bar
- exitUntilCollapsed - Better content visibility
- pinned - Persistent header (search bars)
- Provide meaningful navigation icons - Users expect back button on detail screens
- Limit action icons - Max 3-4 icons to avoid crowding
Conclusion
Master TopAppBar variants and scroll behaviors to create professional, modern app bar UIs in Jetpack Compose Material3.
8 Android App Templates → https://myougatheax.gumroad.com
Top comments (0)