What I want to do is something like this in the Google Photos app:
At first, the status bar and top app bar take up some space:
When starting to scroll, the top app bar disappears towards the top of the screen:
After scrolling for a bit, the top app bar disappears completely and the whole screen is used to show the actual content. The status bar is still being drawn, but transparent with a slight tint:
It looks like the usual "scroll top bar away" is achieved by setting
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
on the Scaffold containing the TopAppBar. What I'm having difficulties with is making the status bar transparent while being able to scroll the TopAppBar behind the status bar like in the second image. The first attempt came pretty close, with the status bar background being the same color as the TopAppBar:
But when scrolling, the TopAppBar disappears instead of going behind the status bar:
Scrolling further gives the status bar this weird default(?) color, even though I've defined it to be transparent at different points:
Combining different settings of windowInsets / windowInsetPadding, Scaffold's inner padding and the scrollBehaviour, the best I was able to do was to make the status bar transparent:
This is done by setting .windowInsetsPadding(WindowInsets.statusBars.only(WindowInsetsSides.Top)) on TopAppBars modifier and not applying Scaffolds inner padding to the LazyColumn. This leads to the TopAppBar disappearing instead of going behind the status bar and the content from below the TopAppBar being drawn behind the status bar).
I also manage to make the TopAppBar start at the very top of the screen, but have the text etc. on it be behind the status bar from the start. I can't remember how to reproduce it right now, but it seemed promising. The only problem was that the TopAppBar was not 'tall' enough to extend behind the status bar all the way. I tried playing with its height / minheight and padding but couldn't get it right.
Gemini has me running in circles about this. I've reduced it to a minimal project with this code, some options and debug logs - nothing seems to work right:
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.Transparent.toArgb()
window.navigationBarColor = Color.Transparent.toArgb()
setContent {
MinimalTheme {
//val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState())
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
Scaffold(
modifier = Modifier
.fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection),
containerColor = Color.Magenta,
topBar = {
TopAppBar(
title = { Text("My App") },
modifier = Modifier
.windowInsetsPadding(WindowInsets.statusBars.only(WindowInsetsSides.Horizontal)) // or .Top
.fillMaxWidth(),
//.height(80.dp),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Red,
titleContentColor = Color.White
),
scrollBehavior = scrollBehavior
//windowInsets = TopAppBarDefaults.windowInsets.only(WindowInsetsSides.top)
)
}
) { scaffoldInnerPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(scaffoldInnerPadding)
.background(Color.Gray),
contentPadding = PaddingValues(16.dp)
) {
items(50) { index ->
Text(
"Item $index",
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
color = Color.Black
)
}
}
}
}
}
}
}
@Composable
fun MinimalTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
Log.d("TransparencyTest", "MinimalTheme SideEffect - START")
val window = (view.context as? android.app.Activity)?.window
if (window == null) {
Log.e("TransparencyTest", "Window is NULL in SideEffect")
return@SideEffect
}
window.statusBarColor = android.graphics.Color.TRANSPARENT // Use framework's transparent
Log.d("TransparencyTest", "statusBarColor RE-SET TO TRANSPARENT in SideEffect")
val insetsController = WindowInsetsControllerCompat(window, view)
insetsController.isAppearanceLightStatusBars = !darkTheme
Log.d("TransparencyTest", "isAppearanceLightStatusBars SET TO ${!darkTheme}")
insetsController.isAppearanceLightNavigationBars = !darkTheme
Log.d("TransparencyTest", "MinimalTheme SideEffect - END")
}
}
MaterialTheme(
colorScheme = if (darkTheme) darkColorScheme() else lightColorScheme(),
typography = Typography(),
content = content
)
}
Edit: I think Edric's comment is at least part of the solution. Setting the LazyColumn's contentPadding to Scaffold's padding, results in the list not starting at the very top, but below the TopAppBar:
(This is in combination with
.windowInsetsPadding(WindowInsets.statusBars.only(WindowInsetsSides.Top))
on the TopAppBar's modifier)
I think the only step that's left is extending the TopAppBar so it is seen below the status bar. Right now, the TopAppBar still disappears 'into' the invisible status bar.
Edit 2: Is it possible that it's not a TopAppBar at all, but just a Box or something that works like a TopAppBar? Is it possible to connect other elements with nestedScrolling?










contentPaddingargument toLazyColumnas well as consuming the window insets with theconsumeWindowInsetsmodifier perhaps, like the example code in theScaffolddocumentationLazyColumn's content behind the status bar - check my edit.