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.