Building a News App with Search and Category Filters
Course: Android App Development
Topic: Building a News App with Search and Category Filters
Course Link: Android App Development Course
Demo Registration: Register for a Free Course Demo
Website: JustAcademy Homepage
1. Introduction to Building a News App
A News App is an Android application that allows users to read news articles from different sources, explore news by category, search for specific topics, and open complete articles for detailed reading. It may display headlines, descriptions, publication dates, source names, and article images.
In this project, we will develop a News App using Kotlin and Android development tools. The application will retrieve news from a news API, display articles in a structured list, and allow users to filter content using a search field and category selection controls.
The user interface can be implemented using Jetpack Compose or XML layouts. The examples in these notes use Kotlin with Jetpack Compose, ViewModel, Kotlin Coroutines, and a repository-based architecture.
Key Features
- Display the latest available news articles from a configured API.
- Search articles using keywords such as technology, cricket, education, or business.
- Filter news by categories such as General, Business, Technology, Sports, Health, and Entertainment.
- Display article headlines, images, descriptions, sources, and publication dates.
- Open the original article in a browser or a suitable in-app browser.
- Show loading indicators, empty search results, and error messages.
- Optionally save articles as bookmarks and cache news for offline reading.
2. Learning Objectives
After completing this topic, learners should be able to:
- Understand the architecture of a news application.
- Connect an Android application to a news API.
- Retrieve and parse JSON responses.
- Build reusable news article cards.
- Implement search using user-entered keywords.
- Apply category filters to a list of articles.
- Manage application state with ViewModel and StateFlow.
- Handle API errors, empty results, and network connectivity problems.
- Test search and category filtering independently and together.
3. Technologies and Tools Required
| Technology |
Purpose |
| Android Studio |
Development environment for building and testing the application. |
| Kotlin |
Programming language used to implement application logic. |
| Jetpack Compose |
Declarative UI toolkit for building the news interface. |
| ViewModel |
Maintains UI-related data across configuration changes. |
| StateFlow |
Exposes observable application state to the UI. |
| Kotlin Coroutines |
Performs asynchronous network and data operations. |
| Retrofit or another HTTP client |
Sends requests to a news API and receives responses. |
| Kotlin Serialization or Moshi |
Converts JSON responses into Kotlin data objects. |
| Coil |
Loads article images from remote URLs in Compose. |
| News API provider |
Supplies news headlines, article metadata, and category results. |
Jetpack libraries support a modern Android architecture, while ViewModel and reactive state management help separate application logic from the user interface. See the official Android Jetpack documentation.
4. Understanding News APIs
An API, or Application Programming Interface, allows the Android application to request news data from a remote service. The provider processes the request and returns a response, commonly in JSON format.
Example API Request
GET https://newsapi.org/v2/top-headlines?country=us&category=technology&apiKey=YOUR_API_KEY
This is an illustrative request format. Use an API provider's current documentation to confirm supported countries, categories, authentication requirements, and usage limits. Do not place a production API secret directly in a publicly distributed Android application.
Example JSON Response
{
"status": "ok",
"totalResults": 1,
"articles": [
{
"source": {
"name": "Example News"
},
"author": "Editorial Team",
"title": "New Technology Trends",
"description": "A summary of recent technology developments.",
"url": "https://example.com/news/technology",
"urlToImage": "https://example.com/images/news.jpg",
"publishedAt": "2026-10-29T08:30:00Z",
"content": "Sample article content..."
}
]
}
The values above are sample data, not a live news feed. The API response may contain missing fields, so the app should handle optional article images, authors, descriptions, and publication dates.
5. Designing the Application Architecture
A maintainable News App separates the user interface, business logic, and data retrieval. A simple architecture can use the following layers:
News App
|
v
Jetpack Compose UI
|
v
NewsViewModel
|
v
NewsRepository
|
v
NewsApiService
|
v
News API Server
| Component |
Responsibility |
| Compose UI |
Displays article cards, search input, category controls, and status messages. |
| ViewModel |
Stores search text, selected category, loading state, and article results. |
| Repository |
Coordinates data retrieval and any local cache. |
| API service |
Defines HTTP endpoints and request parameters. |
| Data model |
Represents article information and API responses. |
6. Setting Up the Android Project
- Open Android Studio and create a new Android project.
- Select a Kotlin-based project with Jetpack Compose enabled.
- Choose a suitable minimum SDK based on the app's requirements.
- Configure dependencies for networking, JSON parsing, ViewModel, image loading, and Compose.
- Create packages for data models, API services, repositories, ViewModels, and UI components.
- Obtain an API key from a news provider that supports your intended use.
- Test the API request independently before connecting it to the UI.
Internet Permission
Add the following permission to the AndroidManifest.xml file, outside the application element:
<uses-permission android:name="android.permission.INTERNET" />
For production applications, use HTTPS, protect credentials, and follow the news provider's licensing, attribution, caching, and rate-limit requirements.
7. Creating the News Data Model
The data model defines the structure of each news article used by the app. The following simplified Kotlin model uses nullable fields for information that might be missing from an API response.
data class NewsArticle(
val sourceName: String?,
val author: String?,
val title: String,
val description: String?,
val articleUrl: String,
val imageUrl: String?,
val publishedAt: String?
)
data class NewsResponse(
val status: String,
val totalResults: Int,
val articles: List<NewsArticle>
)
These classes illustrate the desired application model. Real API responses may use nested source objects and different JSON field names. Configure serialization annotations or map provider-specific response objects into this app model.
Why Data Models Are Important
- They provide a consistent structure for article data.
- They reduce repeated handling of raw JSON.
- They improve readability and maintainability.
- They make it easier to test filtering and display logic.
8. Creating the API Service
The API service defines the endpoints used to retrieve news. Retrofit is one option for declaring HTTP requests as Kotlin interfaces.
interface NewsApiService {
@GET("v2/top-headlines")
suspend fun getTopHeadlines(
@Query("country") country: String,
@Query("category") category: String?,
@Query("apiKey") apiKey: String
): NewsApiResponse
}
This interface is a conceptual example. It requires Retrofit and JSON-converter dependencies, the correct imports, a base URL, and a provider-specific response model. The API key should be supplied through an appropriate configuration or backend strategy rather than hard-coded into source code.
Example Request Parameters
| Parameter |
Meaning |
Example |
| country |
Country for top headlines, if supported by the provider. |
us |
| category |
News category supported by the endpoint. |
technology |
| q |
Search phrase, if supported by the endpoint. |
artificial intelligence |
| apiKey |
Authentication credential required by the provider. |
Configured secret |
Do not assume every provider supports every parameter or permits combining search and category filters in the same request.
9. Building the Search Feature
Search allows users to find articles by entering words or phrases. For example, entering “space exploration” should narrow the displayed articles to those matching the query.
Search Workflow
- The user enters text in the search field.
- The app updates the search query state.
- The query is normalized by removing unnecessary surrounding spaces and ignoring case when appropriate.
- The app filters the loaded article list locally or requests matching articles from the API.
- The UI displays matching results or an informative empty state.
Example Local Search Function
fun filterArticlesBySearch(
articles: List<NewsArticle>,
query: String
): List<NewsArticle> {
val normalizedQuery = query.trim()
if (normalizedQuery.isBlank()) {
return articles
}
return articles.filter { article ->
article.title.contains(
normalizedQuery,
ignoreCase = true
) || article.description.orEmpty().contains(
normalizedQuery,
ignoreCase = true
) || article.sourceName.orEmpty().contains(
normalizedQuery,
ignoreCase = true
)
}
}
This function searches the articles already loaded into memory. If the app needs to search all available news, including articles that have not been downloaded, it should use the provider's supported server-side search endpoint.
Search Field UI Example
@Composable
fun NewsSearchField(
query: String,
onQueryChange: (String) -> Unit
) {
OutlinedTextField(
value = query,
onValueChange = onQueryChange,
label = { Text("Search news") },
placeholder = {
Text("Enter a keyword or topic")
},
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
}
This composable requires the relevant Jetpack Compose Material and layout imports.
10. Implementing Category Filters
Category filters help users browse a specific type of news without repeatedly entering search terms. Common categories include General, Business, Technology, Sports, Health, and Entertainment.
Example Category List
val newsCategories = listOf(
"All",
"General",
"Business",
"Technology",
"Sports",
"Health",
"Entertainment"
)
These are example UI labels. Map them to the category values actually supported by your API provider. Some providers use different names or restrict category access based on region or plan.
Category Filtering Logic
When filtering already loaded data, the app needs a category field or a reliable category mapping for each article. A provider may instead return category-specific results when a category is sent with the request.
data class CategorizedArticle(
val article: NewsArticle,
val category: String
)
fun filterByCategory(
articles: List<CategorizedArticle>,
selectedCategory: String
): List<CategorizedArticle> {
if (selectedCategory == "All") {
return articles
}
return articles.filter {
it.category.equals(
selectedCategory,
ignoreCase = true
)
}
}
Do not assign categories based only on a headline unless you clearly treat that classification as an approximation. Prefer category metadata supplied by the API or results fetched from a category-specific endpoint.
Displaying Categories in Compose
@Composable
fun CategoryFilters(
categories: List<String>,
selectedCategory: String,
onCategorySelected: (String) -> Unit
) {
LazyRow(
horizontalArrangement =
Arrangement.spacedBy(8.dp)
) {
items(categories) { category ->
FilterChip(
selected =
selectedCategory == category,
onClick = {
onCategorySelected(category)
},
label = { Text(category) }
)
}
}
}
The selected chip should be visually distinct. Horizontal scrolling helps keep the interface usable when there are many categories or when the device screen is narrow.
11. Combining Search and Category Filters
The app should apply the selected category and search query together. For example, when the user selects Technology and searches for “AI,” the visible results should match both conditions.
fun applyNewsFilters(
articles: List<CategorizedArticle>,
query: String,
selectedCategory: String
): List<CategorizedArticle> {
val normalizedQuery = query.trim()
return articles.filter { item ->
val categoryMatches =
selectedCategory == "All" ||
item.category.equals(
selectedCategory,
ignoreCase = true
)
val searchableText = listOfNotNull(
item.article.title,
item.article.description,
item.article.sourceName
).joinToString(" ")
val queryMatches =
normalizedQuery.isBlank() ||
searchableText.contains(
normalizedQuery,
ignoreCase = true
)
categoryMatches && queryMatches
}
}
Combined Filtering Example
| Selected Category |
Search Query |
Expected Result |
| All |
Empty |
All currently loaded articles. |
| Technology |
Empty |
Articles in the Technology category. |
| All |
Cricket |
Loaded articles matching “Cricket”. |
| Sports |
Cricket |
Sports articles matching “Cricket”. |
| Business |
Technology |
Business articles matching “Technology”, if any. |
12. Managing State with ViewModel
A ViewModel can hold the search query, selected category, article list, and loading or error status. This keeps business logic outside the composable UI and helps preserve state during configuration changes.
data class NewsUiState(
val articles: List<CategorizedArticle> = emptyList(),
val query: String = "",
val selectedCategory: String = "All",
val isLoading: Boolean = false,
val errorMessage: String? = null
)
The ViewModel should expose an observable state, such as StateFlow, and update it when the user searches, selects a category, refreshes the feed, or encounters an error.
State Management Responsibilities
- Store the current search query.
- Track the selected category.
- Expose loading and error states.
- Request news through a repository.
- Combine search and category filters consistently.
- Avoid performing network requests directly inside repeatedly recomposed UI functions.
13. Creating News Article Cards
Each article card should present important information clearly and allow the user to open the complete story.
Recommended Card Content
- Article thumbnail, when available.
- Headline.
- Short description.
- News source.
- Publication date or relative time.
- Optional bookmark control.
@Composable
fun NewsArticleCard(
article: NewsArticle,
onOpenArticle: (String) -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable {
onOpenArticle(article.articleUrl)
}
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = article.title,
style = MaterialTheme.typography.titleMedium
)
article.description?.let { description ->
Spacer(Modifier.height(8.dp))
Text(
text = description,
style = MaterialTheme.typography.bodyMedium
)
}
Spacer(Modifier.height(8.dp))
Text(
text = article.sourceName ?: "Unknown source",
style = MaterialTheme.typography.labelMedium
)
}
}
}
This basic example omits image loading and complete date formatting. In a finished application, add an image component such as Coil's AsyncImage, provide placeholder and error images, and ensure that the card remains usable when optional fields are missing.
14. Displaying the News Feed
Use LazyColumn to display a scrollable list of articles efficiently. Each article should have a stable key where possible.
@Composable
fun NewsFeed(
articles: List<CategorizedArticle>,
onOpenArticle: (String) -> Unit
) {
if (articles.isEmpty()) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
contentAlignment = Alignment.Center
) {
Text("No articles match your filters.")
}
return
}
LazyColumn(
verticalArrangement =
Arrangement.spacedBy(12.dp)
) {
items(
items = articles,
key = { it.article.articleUrl }
) { item ->
NewsArticleCard(
article = item.article,
onOpenArticle = onOpenArticle
)
}
}
}
Import the required Compose layout, foundation, and Material components. Article URLs should be unique and stable for the selected data source; if they are not, use a suitable unique identifier.
15. Opening the Full News Article
Many news APIs return a link to the publisher's original article rather than the complete article text. The app can open that link in the user's browser or a supported in-app browser.
fun openArticle(
context: Context,
articleUrl: String
) {
val uri = Uri.parse(articleUrl)
if (uri.scheme != "https" && uri.scheme != "http") {
return
}
val intent = Intent(
Intent.ACTION_VIEW,
uri
)
try {
context.startActivity(intent)
} catch (exception: ActivityNotFoundException) {
Toast.makeText(
context,
"No browser is available.",
Toast.LENGTH_SHORT
).show()
}
}
Validate URLs before opening them. In a production app, also restrict unexpected hosts or schemes where appropriate and follow the publisher's terms for article access and display.
16. Handling Loading, Empty, and Error States
Network requests may take time or fail. The app should communicate its current status instead of showing an unexplained blank screen.
| State |
Suggested UI |
Recommended Behaviour |
| Loading |
Progress indicator or skeleton cards. |
Prevent confusing repeated requests and retain relevant context. |
| Successful results |
Article list. |
Display articles and active filters. |
| No search matches |
“No articles match your search.” |
Allow users to clear or change the query. |
| Network error |
Connection message and Retry button. |
Offer retry and preserve the search or category selection. |
| API error |
Useful error message. |
Check status codes, credentials, rate limits, and provider errors. |
| Missing image |
Placeholder image. |
Keep the card layout consistent. |
17. Local Caching and Offline Support
News caching can improve responsiveness and let users revisit previously downloaded headlines when the network is unavailable. Room is an Android persistence library that provides an abstraction over SQLite. See the Room persistence documentation.
A practical caching implementation can store the article identifier, title, description, source, URL, image URL, category, and publication timestamp. Store a last-updated time so the app can explain when cached content was retrieved.
- Display cached results while a refresh is running.
- Replace or update cached articles after a successful response.
- Deduplicate articles using a stable identifier or canonical URL.
- Set reasonable cache expiration rules.
- Do not imply that cached articles are live or current.
- Respect the news provider's caching and licensing conditions.
18. Improving Search and Filtering Performance
- Use a short debounce interval for remote search requests to avoid sending a request for every keystroke.
- Cancel or ignore outdated requests when a newer query is submitted.
- Use case-insensitive matching for local text search.
- Apply category and query conditions consistently.
- Use server-side search for large news collections.
- Use pagination when the provider returns large result sets.
- Keep network operations off the main thread.
- Use stable list keys to reduce unnecessary UI work.
- Consider caching frequently requested categories.
19. Security and Responsible News Display
- Use HTTPS for API communication.
- Avoid exposing privileged API keys in the application package. Use a backend when the provider's credentials require protection.
- Never trust article titles, image URLs, or external links as inherently safe.
- Handle unexpected or malformed API responses safely.
- Respect article copyrights, publisher attribution, and provider usage limits.
- Do not reproduce full articles unless the provider and publisher permit it.
- Clearly distinguish published content from advertisements or sponsored content.
- Do not label a list “Latest News” unless the data source and refresh behaviour support that claim.
20. Testing the News App
Functional Testing
| Test Case |
Steps |
Expected Result |
| Initial feed |
Launch the app with a working connection. |
Articles load and appear in the feed. |
| Keyword search |
Search for “technology”. |
Matching loaded articles are displayed or server results are returned. |
| Category filter |
Select Sports. |
Only results matching the selected category remain. |
| Combined filters |
Select Business and enter a keyword. |
Results satisfy both active conditions. |
| Clear search |
Remove the query. |
The list returns to results for the selected category. |
| No matches |
Enter a phrase unlikely to occur. |
An empty state is displayed. |
| Offline mode |
Disable the network and refresh. |
The app shows an error or cached results when available. |
| Open article |
Select an article card. |
The correct valid article URL is opened. |
| Missing image |
Load an article without an image URL. |
A placeholder appears without crashing. |
| Rapid search |
Type and change queries quickly. |
Old responses do not incorrectly replace the newest results. |
Unit Testing Example
@Test
fun searchMatchesTitleWithoutCaseSensitivity() {
val article = NewsArticle(
sourceName = "Example News",
author = null,
title = "New Technology Trends",
description = "Industry updates",
articleUrl = "https://example.com/article",
imageUrl = null,
publishedAt = null
)
val results = filterArticlesBySearch(
articles = listOf(article),
query = "technology"
)
assertEquals(1, results.size)
}
This test checks the local search helper from the earlier section. Ensure the test imports the appropriate assertion and testing framework, and keep test fixtures consistent with the project's model.
21. Common Problems and Troubleshooting
| Problem |
Possible Cause |
Solution |
| News does not load. |
Missing Internet permission, invalid API key, or connectivity issue. |
Inspect Logcat, verify the request, and check the provider's response. |
| Search returns no results. |
Search is limited to the loaded articles or matching is too restrictive. |
Check the query logic and use a supported remote search endpoint when needed. |
| Category filter returns incorrect items. |
Category metadata is missing or incorrectly mapped. |
Use provider category metadata or fetch results for the selected category. |
| Images do not display. |
Invalid image URL, unsupported response, or missing image-loading configuration. |
Check URLs, network access, image-loader dependencies, and placeholders. |
| App freezes during refresh. |
Blocking work is being performed on the main thread. |
Use suspend functions and coroutines with appropriate dispatchers. |
| Old search results appear. |
Requests complete out of order. |
Cancel outdated work or use latest-query-wins state handling. |
| API rejects requests. |
Rate limits, authentication problems, or unsupported parameters. |
Check provider documentation, status codes, and request limits. |
22. Best Practices
- Keep UI components small and reusable.
- Separate API responses from the app's domain models.
- Use ViewModel and observable UI state for predictable updates.
- Represent loading, success, empty, and error states explicitly.
- Debounce remote search and avoid unnecessary network requests.
- Use pagination for large datasets.
- Support accessibility with readable text, meaningful descriptions, and clear touch targets.
- Test filtering logic independently from network code.
- Handle nullable fields and invalid URLs defensively.
- Keep API credentials and provider-specific configuration out of public source control.
23. Practical Project Exercise
Project: Build a News App with Search and Category Filters.
Project Requirements
- Create a home screen with a news feed.
- Display each article's headline, source, description, and optional thumbnail.
- Add a search field for matching headlines and descriptions.
- Add horizontally scrollable category chips.
- Combine search and category selection so both filters work together.
- Retrieve news from a suitable API and show a loading indicator.
- Handle empty results and API failures.
- Open the original story when an article is selected.
- Optionally add bookmarks and a Room-based local cache.
- Test the app on an emulator or Android device.
Suggested Package Structure
com.example.newsapp/
data/
NewsApiService.kt
NewsRepository.kt
NewsArticle.kt
ui/
NewsScreen.kt
NewsArticleCard.kt
CategoryFilters.kt
NewsViewModel.kt
MainActivity.kt
Expected Outcome
At the end of the exercise, the app should fetch and display news, let users search and filter articles, respond appropriately to errors, and provide access to the publisher's original article. The finished project can be added to an Android development portfolio.
24. Interview Questions and Answers
Q1. What is a News API?
A News API is a service that allows applications to request news-related data, such as headlines, descriptions, categories, sources, and article URLs.
Q2. What is the difference between local and server-side search?
Local search filters data already downloaded to the device. Server-side search sends a query to an API and can retrieve matching articles that are not currently loaded locally.
Q3. Why should category filters and search work together?
Combining both conditions lets users narrow a category to a particular topic, such as finding cricket news within Sports.
Q4. What is ViewModel used for?
ViewModel stores and manages UI-related state outside composable functions, helping separate presentation logic from the interface.
Q5. Why use Kotlin Coroutines?
Coroutines support asynchronous work such as network requests without blocking the main thread when used with appropriate APIs and dispatchers.
Q6. What is the purpose of a repository?
A repository provides a consistent data-access layer and can coordinate remote API requests, local storage, caching, and data mapping.
Q7. How should the app handle API failures?
It should detect errors, display a useful message, provide a retry option, and show cached content if appropriate and available.
Q8. Why are stable keys useful in LazyColumn?
Stable keys help Compose associate list items with their identity when data changes, supporting more predictable and efficient updates.
Q9. How can offline access be supported?
The app can store previously retrieved articles in a local database such as Room and clearly indicate when the displayed data was last updated.
Q10. How can an API key be protected?
A privileged secret should not be embedded directly in a distributed client app. A backend can securely hold the key and enforce access controls when supported by the provider's terms.
25. Quick Revision Checklist
- Understand the news API and its response structure.
- Configure the Internet permission and networking dependencies.
- Create article data models.
- Implement API calls through a repository.
- Display articles using reusable Compose components.
- Implement case-insensitive keyword search.
- Implement category selection and combined filtering.
- Manage UI state with ViewModel.
- Handle loading, errors, and empty results.
- Test article navigation and malformed or missing fields.
- Review security, accessibility, attribution, and API usage rules.
26. Conclusion
Building a News App with Search and Category Filters is a practical Android project that combines API integration, Kotlin programming, UI design, state management, and testing. By using a clear architecture, reliable data models, and reusable Compose components, developers can create an app that makes news easier to browse and search.
Additional features such as bookmarks, pagination, dark mode, offline caching, and personalized categories can extend the project into a more complete application. For structured learning and hands-on Android development training, visit JustAcademy, explore the Android App Development Course, or register for a course demo.