1

I'm trying to implement something that I feel should be easy but I'm having a hard time.

I have a screen with a scaffold, topbar, and lazycolumn as content:

App using scaffold with M3 TopAppBar and LazyColumn as content

I'm using a M3 TopAppBar with a enterAlwaysScrollBehavior so it disappears/appears when the content is scrolled.

What I want to do is implement the search button. How can I make that once the search icon is tapped, the "Cards" title turns into a textfield to input a search query?

I've done this easily using a Row and Crossfade instead of TopAppBar, but then I lose the scrollBehavior feature.

Any ideas?

Thanks, and happy new year!

1 Answer 1

2

Did the following:

@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun SearchAppBar() { val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { var isSearch by remember { mutableStateOf(false) } var value by remember { mutableStateOf("") } Crossfade( modifier = Modifier.animateContentSize(), targetState = isSearch, label = "Search" ) { target -> if (!target) { TopAppBar( title = { Text("Cards") }, actions = { IconButton(Icons.Filled.Search) { isSearch = !isSearch } }, scrollBehavior = scrollBehavior, ) } else { TextField( modifier = Modifier .fillMaxWidth() .windowInsetsPadding(TopAppBarDefaults.windowInsets) .layout { measurable, constraints -> val placeable = measurable.measure(constraints) val height = placeable.height * (1 - scrollBehavior.state.collapsedFraction) layout(placeable.width, height.roundToInt()) { placeable.place(0, 0) } }, value = value, placeholder = { Text("Enter card name") }, onValueChange = { value = it }, leadingIcon = { IconButton(Icons.AutoMirrored.Filled.ArrowBack) { isSearch = !isSearch } }, trailingIcon = if (value.isNotBlank()) { { IconButton(Icons.Filled.Close) { value = "" } } } else { null } ) } } }, ) { paddingValues -> LazyColumn( Modifier .fillMaxSize() .padding(paddingValues) ) { items(200) { index -> Text(modifier = Modifier.fillMaxWidth(), text = "Title $index") } } } } @Composable fun IconButton(imageVector: ImageVector, onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = imageVector, contentDescription = null ) } } 
Sign up to request clarification or add additional context in comments.

4 Comments

it works, thanks! Wondering why there isn't a more intuitive way to achieve this. Seems a pretty common topbar usage for it to be this “tricky” @dmortal
@FerranLP Unfortunately I don't work for Google, so I can't give you an exact answer. I think they make some common component, where you can add any action, not only search, but also menu for example (with Navigation drawer opening).
This doesn't work for me :( I get empty topbar when I use this code. It seems that topBar parameter is expecting a @Composable () -> Unit parameter and then executes what is inside, so, as soon as I add the Crossfade {} wrapper, it stops showing any appbar. Any suggestion on how to fix this?
OK. I found the issue...Apparently, my CustomizedAppBar was already returning a () -> Unit function instead of returning just Unit, so I changed the returning type of the CustomizedAppBar and now I could do it like this. Thank you very much!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.