Sparround

Writing Compose UI with the agent

Compose is relatively friendly territory for an agent: the code is declarative, concentrated in one file, and quickly checked with @Preview. But there are typical mistakes, and they compile — meaning only review and tests catch them:

  • Broken state hoisting — the Composable keeps its state inside, so it can't be reused or tested.
  • Recomposition problems — computation directly in the Composable body without remember.
  • Modifier not threaded properly — not accepted as a parameter, or not applied first in the chain.
  • Lifecycle-unaware collectioncollectAsState keeps collecting in the background.
kotlin
// WHAT THE AGENT OFTEN WRITES (compiles, but has problems)
@Composable
fun OrderScreen(viewModel: OrderViewModel) {
    val state by viewModel.uiState.collectAsState()   // not lifecycle-aware
    var filter by remember { mutableStateOf("") }      // state trapped inside
    val sorted = state.orders.sortedBy { it.date }     // re-sorts on every recomposition

    Column {
        // no modifier parameter — the caller cannot control sizing
        sorted.forEach { OrderRow(it) }
    }
}

// AFTER REVIEW
@Composable
fun OrderScreen(
    viewModel: OrderViewModel,
    modifier: Modifier = Modifier,
) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    OrderScreen(
        state = state,
        onFilterChange = viewModel::onFilterChange,
        modifier = modifier,
    )
}

// The stateless version: testable and previewable
@Composable
fun OrderScreen(
    state: OrderUiState,
    onFilterChange: (String) -> Unit,
    modifier: Modifier = Modifier,
) {
    val sorted = remember(state.orders) { state.orders.sortedBy { it.date } }
    Column(modifier = modifier) {
        sorted.forEach { OrderRow(order = it) }
    }
}

The typical gap. `collectAsStateWithLifecycle` comes from the `androidx.lifecycle:lifecycle-runtime-compose` artifact and ties collection to the lifecycle.

What to state in the taskWhy
A template screen file ("do it like ProfileScreen.kt")The project's state and navigation pattern gets reproduced automatically
Splitting stateful and stateless versionsTesting and previewing are only possible on the stateless version
Requiring `@Preview`Fast visual verification and documentation
Strings must live in `strings.xml`The agent tends to hardcode strings
The compile commandThe agent can verify itself

@Preview is especially valuable when working with an agent: previews get compiled, so syntax and type errors surface immediately, and you can eyeball the result in Android Studio. That makes the "a preview for every public Composable" rule more useful with an agent than without one.

📚 Sources and documentation