Android Jetpack Compose Filter Tutorial
Introduction
Hello! π
In this small tutorial I will show you how to implement a simple list filter search using the latest Android Jetpack. π
Well then let's get started! π
Creating the Project
First we need to actually create the project, open up Android Studio and create a new project.
Make the new project a "Empty Compose Activity" and click on Next.
Next give your new project a name and click on Finish.
Entering The Code
Now that we have the new project up and running. Creating a new directory in the root of the directory called "components" and create a new Kotlin file called "SearchBar.kt", this will be the component that has a text field and button that will allow the users to enter a search word.
package com.example.components
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.example.searchbartutorial.R
@Composable
fun SearchBar(
onSearch: (String) -> Unit
) {
var searchQuery by remember {
mutableStateOf("")
}
Column() {
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
value = searchQuery,
onValueChange = { searchQuery = it },
placeholder = { stringResource(id = R.string.search_bar_label) },
modifier = Modifier.focusable(true),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
maxLines = 1,
leadingIcon = { Icon(Icons.Default.Search, contentDescription = "") }
)
Spacer(modifier = Modifier.width(10.dp))
Button(
onClick = {
onSearch(searchQuery)
}
) {
Text(text = stringResource(id = R.string.search_button))
}
}
}
}
What this does is simply remember what the user searched for and when the user enters an input and clicks on Search it will call the function passed to it. The strings.xml file we are using is the following:
<resources>
<string name="app_name">SearchBarTutorial</string>
<!-- for search bar -->
<string name="search_bar_label">Enter keywords</string>
<string name="search_button">Search</string>
<!-- for home -->
<string name="no_users_text">No Users Found</string>
</resources>
We also have the strings needed in the MainActivity for this.
Finally we need to actually use our newly created component in the Main Activity.
package com.example.searchbartutorial
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.components.SearchBar
import com.example.data.UserData
import com.example.searchbartutorial.ui.theme.SearchBarTutorialTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SearchBarTutorialTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
MainScreen()
}
}
}
}
}
@SuppressLint("UnrememberedMutableState", "MutableCollectionMutableState")
@Composable
fun MainScreen() {
val users = mutableStateListOf<UserData>()
val user1 = UserData(1, "ethan")
val user2 = UserData(2, "bob")
var filteredUsers = remember {
mutableStateOf(mutableListOf<UserData>(user1, user2))
}
users.add(user1)
users.add(user2)
Scaffold() {
Column {
Spacer(modifier = Modifier.height(20.dp))
SearchBar(onSearch = {
val result = users.filter { user ->
user.name.lowercase().contains(it.lowercase())
}
if (result.isNotEmpty()) {
filteredUsers.value = result.toMutableStateList()
} else {
filteredUsers.value = mutableListOf<UserData>()
}
})
Spacer(modifier = Modifier.height(10.dp))
if (filteredUsers.value.size == 0) {
Text(
text = stringResource(id = R.string.no_users_text),
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
fontSize = 22.sp
)
return@Column
}
filteredUsers.value.map {
Text(
text = it.name,
fontWeight = FontWeight.Bold,
fontSize = 22.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(5.dp))
}
}
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
SearchBarTutorialTheme {
MainScreen()
}
}
Here we actually use our newly created SearchBar component to filter a user list. βΊοΈ We also need to make sure to handle when no users are found.
That's it. With just a little bit of code we were able to filter a list of users. π
Conclusion
Here I have shown how to implement a simple using search filter. I actually needed to something like this for work and I had a lot of problems with Exceptions etc, so I decided to write this up for my future self and I hope this is of use to you so you don't make the same mistakes I did. π£
You can also find the source code for this directory here: https://github.com/ethand91/android-compose-filter
Link to the original article: https://ethan-dev.com/post/android-jetpack-compose-filter-tutorial
Like me work? I post about a variety of topics, if you would like to see more please like and follow me. Also I love coffee.
If you are looking to learn Algorithm Patterns to ace the coding interview I recommend the following course: https://algolab.so/p/algorithms-and-data-structure-video-course?affcode=1413380_bzrepgch