I have a requirement to collapse both TopAppbar and Search view and the tab layout should be sticky there. I tried different ways but Searchview is not collapsing, it is pinned below MediumTopApp bar.
[enter image description here](https://i.sstatic.net/rUq5nDLk.png)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun MyCollapsingLayout() {
// Define state for tabs
val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
var selectedTabIndex by remember { mutableIntStateOf(0) }
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
// LazyColumn state to detect scrolling
val listState = rememberLazyListState()
Scaffold(
topBar = {
// Wrapping both the MediumTopAppBar and the SearchBar in a single Column
Column(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection) // Attach scroll behavior
.fillMaxWidth()
.zIndex(1f)
) {
// MediumTopAppBar with collapsing behavior
MediumTopAppBar(
title = { Text("Medium AppBar") },
scrollBehavior = scrollBehavior
)
// Search bar is part of the collapsible area
SearchBar()
}
},
content = { padding ->
LazyColumn(
state = listState, // Attach LazyColumn to listen for scrolling
modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
// Sticky header for TabRow, it will stay at the top when scrolled
stickyHeader {
TabRow(
selectedTabIndex = selectedTabIndex,
modifier = Modifier
.fillMaxWidth()
.zIndex(1f) // Ensures the tab stays above scrolling content
) {
tabs.forEachIndexed { index, tab ->
Tab(
selected = selectedTabIndex == index,
onClick = { selectedTabIndex = index },
text = { Text(tab) }
)
}
}
}
// The rest of the scrollable content
items(50) { index ->
ListItem(text = "Item $index")
}
}
}
)
}
@Composable
fun SearchBar() {
// Search Bar composable that will scroll away with the MediumTopAppBar
TextField(
value = "",
onValueChange = {},
placeholder = { Text("Search...") },
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
)
}
@Composable
fun ListItem(text: String) {
Text(
text = text,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
}
I have the requirement to collapse both TopAppBar and SearchView
Now TopAppBr Collapsed but searchview pinned there
The scrollBehaviour
exposes a state variable scrollBehavior.state.collapsedFraction
. It denotes to what percentage the TopAppBar
is collapsed. You can try to use it to animate the height of the TextField
.
First, allow passing in a Modifier
into the SearchBar
Composable:
@Composable
fun SearchBar(modifier: Modifier) {
TextField(
value = "",
onValueChange = {},
placeholder = { Text("Search...") },
modifier = modifier // apply the passed in Modifier here
.fillMaxWidth()
.padding(8.dp)
)
}
Then, call it as follows:
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
Column(
modifier = Modifier.fillMaxWidth()
) {
MediumTopAppBar(
title = { Text("Medium AppBar") },
scrollBehavior = scrollBehavior
)
SearchBar(
modifier = Modifier
.height((TextFieldDefaults.MinHeight + 16.dp) * (1 - scrollBehavior.state.collapsedFraction))
.clipToBounds()
.alpha(1 - scrollBehavior.state.collapsedFraction)
)
}
},
content = { padding ->
//...
}
)
Also note that I needed to apply the nestedScroll
Modifier on the Scaffold
to get it to work.
Output:
Note:
The height from the TextField
is coming from 56.dp
height plus 8.dp
padding at the top and bottom according to the Material Design Guidelines.