Sparround

Compose fundamentals: composables, recomposition, state

Jetpack Compose is declarative UI: you describe the screen as a function of state, and the framework applies changes itself. The imperative XML model of "find the view, update its value" is gone.

Core concepts:

  • @Composable functions — UI's building blocks; they emit UI, return nothing; named in UpperCamelCase.
  • Recomposition — when observed state changes, Compose re-invokes only the composables that read it. Smart skipping: unchanged parameters (equals on stable types) let a composable be skipped.
  • State: remember { mutableStateOf(...) } — a value surviving recompositions; rememberSaveable — surviving configuration changes/process death.
  • State hoisting — lift state up: a child takes value + onValueChange and holds no state itself → reuse and testing get easier.
  • Stateless vs stateful composables: stateless ones are ideal for previews and tests; stateful ones should be thin wrappers.
kotlin
// Stateless — yenidənistifadə olunan, test olunan
@Composable
fun AmountField(
    amount: String,
    onAmountChange: (String) -> Unit,
    modifier: Modifier = Modifier
) {
    OutlinedTextField(
        value = amount,
        onValueChange = onAmountChange,
        label = { Text("Məbləğ") },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
        modifier = modifier
    )
}

// Sahib — state ViewModel-dədir
@Composable
fun TransferScreen(viewModel: TransferViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()

    AmountField(
        amount = uiState.amount,
        onAmountChange = viewModel::onAmountChanged
    )
}

State hoisting: a stateless field + a stateful owner

Two classic traps: (1) mutableStateOf without remember — state resets every recomposition (endless reset); (2) mixing up reading via the by delegate or .value. A third arrives as an interview question: when is recomposition "too much"? — answer: push state reads as low as possible (only the reading composable recomposes) and avoid unstable parameters (ImmutableList/stable data classes over plain List).

🛠 Practice task

Write your first Compose screen (via ComposeView in an existing project, or in a fresh one):

  • Build a counter button, forget remember and watch the value reset; then add it.
  • Compare remember and rememberSaveable across a rotation.
  • Make a TextField stateless through state hoisting (value + onValueChange) and write a @Preview.
  • Build the list with LazyColumn + key = { it.id }.

Done when: you can compare it against how many files/lines the same screen took in XML.

📚 Sources and documentation