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+onValueChangeand 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.
// 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
rememberand watch the value reset; then add it. - Compare
rememberandrememberSaveableacross a rotation. - Make a
TextFieldstateless 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
- Thinking in Composeofficialdeveloper.android.com
- State in Composeofficialdeveloper.android.com
- Lifecycle and Composeofficialdeveloper.android.com