Code

Wire the chain — repo → VM → screen (bottom-up)

Now we connect the whole chain: repository → ViewModel → screen. Five files in the code column, as tabs — build them bottom-up (data → presentation), the order they're in. Build the ViewModel before the Repository and you get four compile errors and no idea why; the dependency runs one way.

1. MenuRepository.kt (data) — a thin layer: takes DTOs from PicaApi, hands back clean domain MenuItems. getMenu() is suspend (one network call). Lesson 7 turns this into a Room-backed cache.

2. MenuEvent.kt (presentation) — what can happen: LoadMenu, Retry.

3. MenuUiState.kt (presentation) — the network states as a sealed interface, exactly the shape the cart taught in lesson 3, but for a network call:

sealed interface MenuUiState {
    data object Loading : MenuUiState
    data class Success(val items: List<MenuItem>) : MenuUiState
    data class Error(val message: String) : MenuUiState
}

4. MenuViewModel.kt (presentation) — init { onEvent(LoadMenu) } loads automatically; shows Loading while fetching, then Success(items) or Error.

5. MenuScreen.kt (modify) — the lesson-2 screen no longer takes a static List<MenuItem>; it takes the ViewModel, observes its state, and renders when (state). The card from lesson 2 is carried forward unchanged inside the Success branch — only the data source changed.

Retrofit BY HAND (no Koin yet — that's lesson 6). We still need a live PicaApi. For now, build the chain yourself at the call site — this is throwaway glue that lesson 6 deletes:

// LESSON 5: wired by hand. Koin replaces ALL of this next lesson.
object Network {
    private val json = Json { ignoreUnknownKeys = true }
    private val retrofit = Retrofit.Builder()
        .baseUrl("http://10.0.2.2:8080/")   // emulator → your localhost:8080
        .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
        .build()
    val api: PicaApi = retrofit.create(PicaApi::class.java)
}

// where MenuScreen is shown, hand it a ViewModel built from the chain:
MenuScreen(menuViewModel = viewModel { MenuViewModel(MenuRepository(Network.api)) })

Verify. Run the backend (localhost:8080), then the app on an emulator. You should see:

Briefly:  ⟳ (Loading)
Then:     the real pizza list from the server (GET /menu)

The menu's pizzas now come from the server, not from code.

Gotcha (CLEARTEXT). If the backend runs over plain http (not https, like the local 10.0.2.2:8080), the first run crashes:

java.net.UnknownServiceException: CLEARTEXT communication to 10.0.2.2
not permitted by network security policy

Android blocks unencrypted http by default (on recent versions). For development, allow it in AndroidManifest.xml (debug only!):

<application android:usesCleartextTraffic="true" ... >

(A cleaner way is a network_security_config.xml scoped to just 10.0.2.2.) In production the server must be https — then you don't need this. Add it, re-run, and the menu loads.