How to Create Search View with Jetpack compose

Last updated Dec 15, 2021

In this Jetpack compose tutorial we will learn how to Create Search View with Jetpack compose in Android application. 

Search View is basically a text field where user will type any thing and according to value typed in the text field , given below list will be filtered.

 

Let's get started

Step 1: Create android application in android studio

Step 2: Follow step for setup Jetpack Compose with Android Studio

In this example we are managing the data inside listview with Viewmodel and kotlin data classes

Lets create search Item Data

data class SearchData(var name: String? = null, var emailId: String? = null)

 

View Model

package com.example.jetpack.viewmodels

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.example.jetpack.widget.SearchData
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.util.*
import kotlin.collections.ArrayList


class SearchViewModel(appObj: Application) : AndroidViewModel(appObj) {
    private val _searchList = MutableStateFlow(listOf())
    val list = arrayListOf(
        SearchData(
            name = "Ankit Singh", emailId = "an@gmail.com"
        ),
        SearchData(
            name = "Neha Shaw", emailId = "ne@gmail.com"
        ),
        SearchData(
            name = "Arpita Ghosh", emailId = "ar@gmail.com"
        ),
        SearchData(
            name = "Akash Tiwari", emailId = "ak@gmail.com"
        ),
        SearchData(
            name = "Anisha Tiwari", emailId = "an@gmail.com"
        ),
        SearchData(
            name = "Rowdy Rathore", emailId = "ro@gmail.com"
        ),
        SearchData(
            name = "Jit Singh", emailId = "ji@gmail.com"
        ),
        SearchData(
            name = "Pravin Raj", emailId = "pr@gmail.com"
        ),
        SearchData(
            name = "Sneha Rao", emailId = "sn@gmail.com"
        ),
        SearchData(
            name = "Ranjana Rathore", emailId = "ran@gmail.com"
        ),
        SearchData(
            "Kamala Rathore", emailId = "ka@gmail.com"
        )
    )
    val searchList: StateFlow> get() = _searchList

    fun searchedItems(searchedText: String) {
        if (searchedText.isNotEmpty()) {
            val resultList = ArrayList()
            for (data in list) {
                if (data.name?.lowercase(Locale.getDefault())
                        ?.contains(searchedText, ignoreCase = true) == true
                ) {
                    resultList.add(data)
                }
            }
            _searchList.value = resultList
        } else {
            _searchList.value = list
        }
    }

    init {
        fetchList()
    }

    private fun fetchList() {
        viewModelScope.launch {
            _searchList.emit(list)
        }
    }
}

 

Here we have used stateFlow which emits list of serachData whenever search data will changed that can be observed while showing list items.

Lets create UI to display data inside listview

@Composable
fun SearchListItem(searchData: SearchData) {
    Card(
        modifier = Modifier
            .padding(top = 16.dp)
            .fillMaxWidth(),
        elevation = 4.dp,
        border = BorderStroke(width = 1.2.dp, Color.Blue)
    ) {
        Row(content = {
            Icon(
                Icons.Default.Contacts,
                "",
                tint = Color.Blue,
                modifier = Modifier
                    .padding(start = 16.dp, top = 16.dp)
            )
            Column(modifier = Modifier.padding(16.dp)) {
                Text(
                    text = "${searchData.name}",
                    style = TextStyle(
                        color = Color.Blue,
                        fontSize = 21.sp,
                        fontWeight = FontWeight.Bold
                    )
                )
                Text(text = "${searchData.emailId}", modifier = Modifier.padding(top = 8.dp))
            }
        })

    }
}

 

Let's create ui for SearchView ( we will create search view with prefix icon as magnifying glass, in the middle we will use text field and in the end cross icon) with SearchViewTextField composable function where we are going the pass the mutable state so that whenever user type any thing it will be save in state and according to typed data list will be rendered.

 

@Composable
fun SearchViewTextField(state: MutableState) {
    Box(
        modifier = Modifier
            .border(width = 1.dp, color = Color.Gray, shape = CircleShape)
            .fillMaxWidth()
    ) {
        BasicTextField(
            value = state.value,
            onValueChange = {
                state.value = it
            },
            modifier = Modifier
                .background(Color.White, CircleShape)
                .height(38.dp)
                .fillMaxWidth(),
            singleLine = true,
            maxLines = 1,
            decorationBox = { innerTextField ->
                Row(
                    verticalAlignment = Alignment.CenterVertically,
                    modifier = Modifier.padding(horizontal = 10.dp)
                ) {
                    Icon(
                        imageVector = Icons.Default.Search,
                        contentDescription = "image",
                        tint = Color.Blue
                    )
                    Box(
                        modifier = Modifier.weight(1f),
                        contentAlignment = Alignment.CenterStart
                    ) {
                        if (state.value == TextFieldValue("")) Text(
                            "Search"
                        )
                        innerTextField()
                    }
                    if (state.value != TextFieldValue("")) {
                        IconButton(
                            onClick = {
                                state.value = TextFieldValue("")
                            },
                        ) {
                            Icon(
                                imageVector = Icons.Default.Close,
                                contentDescription = "image",
                                tint = Color.Blue
                            )
                        }
                    }
                }
            }
        )
    }
}

 

Create top App bar

TopAppBar(
    title = { Text("Search View Demo") }
)

 

 

Lets combine Viewmodel, SearchView, List together and top app bar

@Composable
fun SearchViewDemo(searchViewModel: SearchViewModel) {
    JetPackTheme(
    ) {
        Scaffold(
            modifier = Modifier.fillMaxSize(),
            content = {
                SearchContent(searchViewModel)

            }, topBar = {
                TopAppBar(
                    title = { Text("Search View Demo") }
                )
            }
        )
    }
}

@Composable
fun SearchContent(searchViewModel: SearchViewModel) {
    val searchList = searchViewModel.searchList.collectAsState()
    val searchBy = remember { mutableStateOf(TextFieldValue("")) }
    Column(
        Modifier
            .padding(top = 16.dp, start = 16.dp, end = 16.dp)
            .fillMaxSize()
    ) {
        SearchViewTextField(searchBy)
        searchViewModel.searchedItems(searchBy.value.text)
        LazyColumn(
            contentPadding = PaddingValues(
                bottom = 100.dp
            )
        ) {
            items(
                items = searchList.value,
                itemContent = {
                    SearchListItem(searchData = it)
                })
        }
    }
}

 

 

 

Complete example code to implement searchview to search the listview items with jetpack compose

package com.example.jetpack

import android.os.Bundle
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Contacts
import androidx.compose.material.icons.filled.Search
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.ViewModelProvider
import com.example.jetpack.ui.theme.JetPackTheme
import com.example.jetpack.viewmodels.SearchViewModel
import com.example.jetpack.widget.SearchViewDemo

class MainActivity : ComponentActivity() {

    @ExperimentalMaterialApi
    @ExperimentalFoundationApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
        val searchViewModel = ViewModelProvider(this).get(SearchViewModel::class.java)
        setContent {
            SearchViewDemo(searchViewModel)
        }
    }
}

@ExperimentalMaterialApi
@ExperimentalFoundationApi
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
}

@Composable
fun SearchViewDemo(searchViewModel: SearchViewModel) {
    JetPackTheme(
    ) {
        Scaffold(
            modifier = Modifier.fillMaxSize(),
            content = {
                SearchContent(searchViewModel)

            }, topBar = {
                TopAppBar(
                    title = { Text("Search View Demo") }
                )
            }
        )
    }
}

@Composable
fun SearchContent(searchViewModel: SearchViewModel) {
    val searchList = searchViewModel.searchList.collectAsState()
    val searchBy = remember { mutableStateOf(TextFieldValue("")) }
    Column(
        Modifier
            .padding(top = 16.dp, start = 16.dp, end = 16.dp)
            .fillMaxSize()
    ) {
        SearchViewTextField(searchBy)
        searchViewModel.searchedItems(searchBy.value.text)
        LazyColumn(
            contentPadding = PaddingValues(
                bottom = 100.dp
            )
        ) {
            items(
                items = searchList.value,
                itemContent = {
                    com.example.jetpack.widget.SearchListItem(searchData = it)
                })
        }
    }
}

@Composable
fun SearchListItem(searchData: SearchData) {
    Card(
        modifier = Modifier
            .padding(top = 16.dp)
            .fillMaxWidth(),
        elevation = 4.dp,
        border = BorderStroke(width = 1.2.dp, Color.Blue)
    ) {
        Row(content = {
            Icon(
                Icons.Default.Contacts,
                "",
                tint = Color.Blue,
                modifier = Modifier
                    .padding(start = 16.dp, top = 16.dp)
            )
            Column(modifier = Modifier.padding(16.dp)) {
                Text(
                    text = "${searchData.name}",
                    style = TextStyle(
                        color = Color.Blue,
                        fontSize = 21.sp,
                        fontWeight = FontWeight.Bold
                    )
                )
                Text(text = "${searchData.emailId}", modifier = Modifier.padding(top = 8.dp))
            }
        })

    }
}

@Composable
fun SearchViewTextField(state: MutableState) {
    Box(
        modifier = Modifier
            .border(width = 1.dp, color = Color.Gray, shape = CircleShape)
            .fillMaxWidth()
    ) {
        BasicTextField(
            value = state.value,
            onValueChange = {
                state.value = it
            },
            modifier = Modifier
                .background(Color.White, CircleShape)
                .height(38.dp)
                .fillMaxWidth(),
            singleLine = true,
            maxLines = 1,
            decorationBox = { innerTextField ->
                Row(
                    verticalAlignment = Alignment.CenterVertically,
                    modifier = Modifier.padding(horizontal = 10.dp)
                ) {
                    Icon(
                        imageVector = Icons.Default.Search,
                        contentDescription = "image",
                        tint = Color.Blue
                    )
                    Box(
                        modifier = Modifier.weight(1f),
                        contentAlignment = Alignment.CenterStart
                    ) {
                        if (state.value == TextFieldValue("")) Text(
                            "Search"
                        )
                        innerTextField()
                    }
                    if (state.value != TextFieldValue("")) {
                        IconButton(
                            onClick = {
                                state.value = TextFieldValue("")
                            },
                        ) {
                            Icon(
                                imageVector = Icons.Default.Close,
                                contentDescription = "image",
                                tint = Color.Blue
                            )
                        }
                    }
                }
            }
        )
    }
}

data class SearchData(var name: String? = null, var emailId: String? = null)

 

 

Images

Searchview implementation with Jetpack compose

Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

2780 Views