Tutorial 8
Build the Phase 1 habit tracker.
This project combines Kotlin data modeling, Compose layout, state, events, and debugging into one small app. It is intentionally simple; the proof is correctness and clarity.
Target screen
Starter implementation
data class Habit(
val id: Int,
val title: String,
val isDone: Boolean = false
)
@Composable
fun HabitTrackerApp() {
var nextId by remember { mutableStateOf(1) }
var title by remember { mutableStateOf("") }
var habits by remember { mutableStateOf(listOf<Habit>()) }
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Habit Tracker")
OutlinedTextField(
value = title,
onValueChange = { title = it },
label = { Text("New habit") }
)
Button(
enabled = title.trim().isNotEmpty(),
onClick = {
habits = habits + Habit(nextId, title.trim())
nextId += 1
title = ""
}
) {
Text("Add habit")
}
if (habits.isEmpty()) {
Text("No habits yet. Add one to start.")
} else {
habits.forEach { habit ->
HabitRow(
habit = habit,
onToggle = {
habits = habits.map {
if (it.id == habit.id) it.copy(isDone = !it.isDone) else it
}
},
onDelete = {
habits = habits.filterNot { it.id == habit.id }
}
)
}
}
}
}Review checklist
- Blank titles cannot be added.
- Adding a habit clears the text field.
- Checking one habit does not change another habit.
- Deleting one habit removes only that habit.
- Empty state appears when the list has no habits.
- README includes screenshots, features, known limitations, and next improvements.