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 policyAndroid 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.xmlscoped to just10.0.2.2.) In production the server must be https — then you don't need this. Add it, re-run, and the menu loads.