Bird
Raised Fist0
Android Kotlinmobile~20 mins

Repository pattern in Android Kotlin - Mini App: Build & Ship

Choose your learning style10 modes available
Build: User List Screen
Shows a list of users fetched using a Repository pattern that abstracts data sources.
Target UI
-------------------------
| User List             |
|-----------------------|
| - Alice               |
| - Bob                 |
| - Charlie             |
|                       |
| [Refresh]             |
-------------------------
Create a User data class with id and name
Create a UserRepository interface with a function to get users
Implement a UserRepositoryImpl that returns a hardcoded list of users
Create a ViewModel that uses the repository to fetch users
Build a UserListScreen composable that shows the list of users
Add a Refresh button to reload the user list
Starter Code
Android Kotlin
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

// TODO: Define User data class here

// TODO: Define UserRepository interface here

// TODO: Implement UserRepositoryImpl here

// TODO: Create UserViewModel here

@Composable
fun UserListScreen() {
    // TODO: Use ViewModel to get users and display list
    // TODO: Add Refresh button
}
Task 1
Task 2
Task 3
Task 4
Task 5
Task 6
Solution
Android Kotlin
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

// Data class representing a User
data class User(val id: Int, val name: String)

// Repository interface to get users
interface UserRepository {
    fun getUsers(): List<User>
}

// Implementation of UserRepository returning hardcoded users
class UserRepositoryImpl : UserRepository {
    override fun getUsers(): List<User> {
        return listOf(
            User(1, "Alice"),
            User(2, "Bob"),
            User(3, "Charlie")
        )
    }
}

// ViewModel class to manage user data
class UserViewModel(private val repository: UserRepository) {
    var users by mutableStateOf<List<User>>(emptyList())
        private set

    fun loadUsers() {
        users = repository.getUsers()
    }
}

@Composable
fun UserListScreen() {
    val repository = remember { UserRepositoryImpl() }
    val viewModel = remember { UserViewModel(repository) }
    var users by remember { mutableStateOf(emptyList<User>()) }

    // Load users initially
    if (users.isEmpty()) {
        viewModel.loadUsers()
        users = viewModel.users
    }

    Column(modifier = Modifier.padding(16.dp)) {
        Text("User List", style = MaterialTheme.typography.headlineMedium)

        LazyColumn(modifier = Modifier.weight(1f)) {
            items(users) { user ->
                Text(text = "- ${user.name}", modifier = Modifier.padding(vertical = 4.dp))
            }
        }

        Button(onClick = {
            viewModel.loadUsers()
            users = viewModel.users
        }) {
            Text("Refresh")
        }
    }
}

This solution uses the Repository pattern to separate data access from UI logic.

We define a User data class to hold user info.

The UserRepository interface declares a method to get users.

UserRepositoryImpl provides a hardcoded list of users.

UserViewModel holds the user list state and loads users from the repository.

The UserListScreen composable displays the users in a list and has a Refresh button to reload the data.

This keeps UI code clean and easy to maintain, following good app architecture.

Final Result
Completed Screen
-------------------------
| User List             |
|-----------------------|
| - Alice               |
| - Bob                 |
| - Charlie             |
|                       |
| [Refresh]             |
-------------------------
User sees a list of three users: Alice, Bob, Charlie
Tapping Refresh reloads the same user list from the repository
Stretch Goal
Add a loading indicator while users are being fetched
💡 Hint
Use a boolean state to track loading and show a Text or CircularProgressIndicator when true

Practice

(1/5)
1. What is the main purpose of the Repository pattern in Android Kotlin development?
easy
A. To separate data access logic from UI and business logic
B. To handle user interface animations
C. To manage app navigation between screens
D. To store UI layout XML files

Solution

  1. Step 1: Understand the role of Repository

    The Repository pattern is designed to manage data operations and provide a clean API to the rest of the app.
  2. Step 2: Identify separation of concerns

    It separates data handling from UI and business logic, making the app easier to maintain and test.
  3. Final Answer:

    To separate data access logic from UI and business logic -> Option A
  4. Quick Check:

    Repository pattern = separation of data logic [OK]
Hint: Repository separates data logic from UI logic [OK]
Common Mistakes:
  • Confusing Repository with UI components
  • Thinking Repository manages navigation
  • Assuming Repository stores layout files
2. Which of the following is the correct way to declare a Repository interface in Kotlin?
easy
A. interface UserRepository { fun getUser(id: Int): User }
B. class UserRepository { fun getUser(id: Int): User }
C. fun UserRepository() { return User }
D. val UserRepository = object { fun getUser(id: Int): User }

Solution

  1. Step 1: Identify interface syntax in Kotlin

    Interfaces in Kotlin are declared with the interface keyword followed by function signatures without bodies.
  2. Step 2: Match the correct declaration

    interface UserRepository { fun getUser(id: Int): User } correctly declares an interface with a function signature. class UserRepository { fun getUser(id: Int): User } is a class, not an interface. Options A, B, and C have incorrect syntax.
  3. Final Answer:

    interface UserRepository { fun getUser(id: Int): User } -> Option A
  4. Quick Check:

    Interface declaration uses interface keyword [OK]
Hint: Interfaces use 'interface' keyword with function signatures [OK]
Common Mistakes:
  • Using class instead of interface for Repository
  • Incorrect function syntax inside interface
  • Trying to instantiate interface directly
3. Given this Kotlin code snippet, what will be printed?
class UserRepository {
  fun getUserName(id: Int): String = if (id == 1) "Alice" else "Unknown"
}

fun main() {
  val repo = UserRepository()
  println(repo.getUserName(1))
  println(repo.getUserName(2))
}
medium
A. Unknown\nAlice
B. Alice\nUnknown
C. 1\n2
D. Alice\nAlice

Solution

  1. Step 1: Analyze getUserName function logic

    The function returns "Alice" if id is 1, otherwise "Unknown".
  2. Step 2: Trace the calls in main()

    Calling getUserName(1) returns "Alice". Calling getUserName(2) returns "Unknown".
  3. Final Answer:

    Alice\nUnknown -> Option B
  4. Quick Check:

    id==1 returns Alice, else Unknown [OK]
Hint: Check condition for id==1 to get 'Alice' [OK]
Common Mistakes:
  • Mixing up return values for different ids
  • Assuming both calls return 'Alice'
  • Confusing id with returned string
4. Identify the error in this Repository implementation:
class UserRepository {
  fun getUser(id: Int): User {
    return User(id, name)
  }
}
Assuming name is not defined anywhere.
medium
A. User class is not imported
B. Missing return type for getUser function
C. Variable 'name' is undefined causing a compilation error
D. Function getUser should be abstract

Solution

  1. Step 1: Check variables used in return statement

    The function returns User(id, name), but name is not declared or passed.
  2. Step 2: Identify compilation error

    Using an undefined variable causes a compilation error in Kotlin.
  3. Final Answer:

    Variable 'name' is undefined causing a compilation error -> Option C
  4. Quick Check:

    Undefined variables cause compile errors [OK]
Hint: Check all variables are defined before use [OK]
Common Mistakes:
  • Ignoring undefined variables
  • Assuming missing imports cause error here
  • Thinking function must be abstract
5. You want to create a Repository that fetches user data from both a local database and a remote API. Which approach best follows the Repository pattern principles?
hard
A. Call the local database and remote API directly from the UI layer
B. Store all user data in the UI components for quick access
C. Create separate Repository classes for local and remote and call both from UI
D. Create a Repository class that decides whether to fetch from local or remote and exposes a single method to get user data

Solution

  1. Step 1: Understand Repository responsibility

    The Repository should act as a single source of truth and hide data sources from the UI.
  2. Step 2: Choose approach that centralizes data logic

    Create a Repository class that decides whether to fetch from local or remote and exposes a single method to get user data centralizes data fetching logic inside the Repository, deciding between local and remote sources.
  3. Step 3: Avoid direct data calls in UI

    Options A and C expose data sources to UI, breaking separation of concerns. Store all user data in the UI components for quick access mixes data with UI, which is bad practice.
  4. Final Answer:

    Create a Repository class that decides whether to fetch from local or remote and exposes a single method to get user data -> Option D
  5. Quick Check:

    Repository centralizes data access [OK]
Hint: Repository hides data sources behind one interface [OK]
Common Mistakes:
  • Calling data sources directly from UI
  • Splitting data logic across UI and multiple classes
  • Storing data inside UI components
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard