flopez
flopez

Reputation: 45

Jetpack Compose TopAppBar with search bar embedded

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!

Upvotes: 0

Views: 147

Answers (1)

dmortal
dmortal

Reputation: 529

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
        )
    }
}

Upvotes: 1

Related Questions