Search code examples
androidkotlinandroid-jetpack-compose

How to add border to SearchBar in Jetpack Compose


I want to create a search bar like this

SearchBar

I have tried this

SearchBar(
    leadingIcon = {
        Icon(
            imageVector = Icons.Default.Search,
            contentDescription = stringResource(id = R.string.image_des)
        )
    },
    trailingIcon = {
        if (isActive) {
            Icon(
                imageVector = Icons.Default.Close,
                contentDescription = stringResource(id = R.string.image_des),
                modifier = Modifier
                    .clickable {
                        isActive = false
                    }
            )
        } else {
            Row(
                verticalAlignment = Alignment.CenterVertically
            ) {
                Box(
                    modifier = Modifier
                        .width(0.8.dp)
                        .fillMaxHeight(0.4f)
                        .background(Color.Gray)
                )
                Icon(
                    imageVector = Icons.Outlined.LocationOn,
                    contentDescription = stringResource(id = R.string.image_des)
                )
                Text(text = location)
            }
        }
    },
    placeholder = {
        Text(text = placeHolder)
    },
    colors = SearchBarDefaults.colors(
        containerColor = Color.White
    ),
    modifier = Modifier
        .fillMaxWidth()
        .padding(10.dp)
        .border(0.5.dp, Color.Black, RoundedCornerShape(30.dp))
) {
}

But my border doesn't match with the SearchBar's container

I wonder if there is any way to achieve my requirement

I look forward to any advice or suggestions. Thank you!


Solution

  • Just a little bit of modification from the Material3 SearchBar. I hope this helps if anybody has a problem like me.

        fun CustomSearchBar(
        query: String,
        onQueryChange: (String) -> Unit,
        onSearch: (String) -> Unit,
        active: Boolean,
        onActiveChange: (Boolean) -> Unit,
        modifier: Modifier = Modifier,
        enabled: Boolean = true,
        placeHolder: @Composable (() -> Unit)? = null,
        leadingIcon: @Composable (() -> Unit)? = null,
        trailingIcon: @Composable (()->Unit)? = null,
        content: @Composable ColumnScope.() -> Unit,
    ) {
        val focusRequester = remember { FocusRequester() }
        val focusManager = LocalFocusManager.current
        Box(
            modifier = modifier
        ) {
            BasicTextField(
                value = query,
                onValueChange = onQueryChange,
                textStyle = TextStyle(
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Normal,
                    color = Color.Black
                ),
                enabled = enabled,
                keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
                keyboardActions = KeyboardActions(onSearch = { onSearch(query) }),
                singleLine = true,
                modifier = Modifier
                    .height(56.dp)
                    .focusRequester(focusRequester)
                    .onFocusChanged { onActiveChange(it.isFocused) }
                    .semantics {
                        onClick {
                            focusRequester.requestFocus()
                            true
                        }
                    },
                decorationBox = { innerTextField ->
                    Box(
                        contentAlignment = Alignment.CenterStart,
                        modifier = Modifier
                            .fillMaxWidth()
                            .border(
                                width = 1.dp,
                                color = Color.LightGray,
                                shape = RoundedCornerShape(size = 30.dp)
                            )
                            .padding(horizontal = 16.dp, vertical = 12.dp) // inner padding
    
                    ) {
                        Box(
                            modifier = Modifier
                                .fillMaxWidth()
                        ) {
                            Row(
                                verticalAlignment = Alignment.CenterVertically,
                                modifier = Modifier
                                    .align(Alignment.CenterStart)
                            ) {
                                leadingIcon?.let { it() }
                                Spacer(modifier = Modifier.width(8.dp))
                                Box {
                                    if (query.isEmpty()) {
                                        placeHolder?.let { it() }
                                    }
                                    innerTextField()
                                }
                            }
                            Box(
                                contentAlignment = Alignment.Center,
                                modifier = Modifier
                                    .align(Alignment.CenterEnd)
                            ) {
                                trailingIcon?.let { it() }
                            }
                        }
                    }
                }
            )
            LaunchedEffect(active) {
                if (!active) {
                    focusManager.clearFocus()
                }
            }
        }
    }