Android Capstone: Articles App
Assemble a complete Android app on the Part 8
architecture: a list/detail experience backed by a ViewModel, an immutable
StateFlow UI state, a repository behind an interface, navigation, and JSON
serialization.
The running architecture core is in examples/android/ (package
com.example.kotlinguide.articles) and builds + unit-tests in CI.
The pieces, end to end
flowchart LR
UI["ArticleListScreen / DetailScreen"] -->|events| VM["ArticlesViewModel"]
VM -->|StateFlow UiState| UI
VM --> REPO["ArticleRepository"]
REPO --> SRC["InMemory / Room / Retrofit"]
1. Model + serialization
data class Article(val id: Int, val title: String, val body: String)
@Serializable
data class ArticleDto(val id: Int, val title: String, val body: String)
2. Repository (interface + implementation)
interface ArticleRepository { suspend fun articles(): List<Article> }
class InMemoryArticleRepository(private val data: List<Article> = sampleArticles) :
ArticleRepository {
override suspend fun articles(): List<Article> = data
}
3. ViewModel + UI state
sealed interface ArticlesUiState {
data object Loading : ArticlesUiState
data class Success(val articles: List<Article>) : ArticlesUiState
data class Error(val message: String) : ArticlesUiState
}
class ArticlesViewModel(private val repository: ArticleRepository) : ViewModel() {
private val _uiState = MutableStateFlow<ArticlesUiState>(ArticlesUiState.Loading)
val uiState: StateFlow<ArticlesUiState> = _uiState.asStateFlow()
fun load() = viewModelScope.launch { /* repository.articles() -> Success/Error */ }
}
4. Navigation + screens
A NavHost wires a list screen (which collects uiState) to a detail screen,
passing the article id as a route argument.
Taking it to production
Swap the in-memory repository for real data sources using the drop-in code from Part 8 — nothing in the ViewModel or UI changes:
- Persistence — add a Room
ArticleDaoand aLocalArticleRepository(see Room). - Networking — add a Retrofit
ArticleApi+RemoteArticleRepository(see Networking). - DI — bind the repository with Hilt and inject the ViewModel (see Hilt).
- Background sync — add a
CoroutineWorker(see WorkManager).
The architecture core (UI, ViewModel, repository interface, serialization, navigation) builds and is unit-tested in CI. Room/Retrofit/Hilt/WorkManager are provided as drop-in code because they need annotation processing and/or the Android runtime — wire them in Android Studio to complete the production app.
Extension challenges
-
Make the detail screen show the full article by looking it up in the repository (add
suspend fun article(id: Int): Article?). -
Add a pull-to-refresh that re-invokes
load(). -
Add a Room-backed cache so the list survives app restarts.
Previous: CLI Capstone · Next: Appendices