-
Notifications
You must be signed in to change notification settings - Fork 0
Feature Screen Pattern
Andrés Pedraza Míguez edited this page Dec 20, 2025
·
2 revisions
To maintain clean code and enable Jetpack Compose @Preview, we separate the "Logic" from the "UI".
- Responsibility: Pure UI rendering.
- Dependencies: None. It relies on simple data classes, primitives, or State objects.
- State: Stateless (receives state as parameters).
-
Events: Exposes lambdas (e.g.,
onCurrencySelected: (String) -> Unit). - Benefit: Can be previewed easily in Android Studio with dummy data.
@Composable
fun DefaultCurrencyScreen(
availableCurrencies: List<Currency>, // Pure data
selectedCurrencyCode: String?, // Pure data
onCurrencySelected: (String) -> Unit // Event
) {
// UI Implementation...
}
- Responsibility: Wiring the logic.
-
Dependencies:
ViewModel,NavController,ScreenUiProvider(viaFeatureScaffold). -
State: Collects
StateFlowfrom the ViewModel. - Events: Handles navigation, side effects, or calls ViewModel functions.
- Benefit: Isolates dependencies from the rendering logic.
@Composable
fun DefaultCurrencyFeature(
viewModel: DefaultCurrencyViewModel = koinViewModel()
) {
// 1. Collect State
val selectedCurrency by viewModel.selectedCurrencyCode.collectAsState()
// 2. Setup Dependencies
val navController = LocalRootNavController.current
val scope = rememberCoroutineScope()
// 3. Wrap with Scaffold (injects TopBar/FAB based on Route)
FeatureScaffold(currentRoute = Routes.SETTINGS_DEFAULT_CURRENCY) {
// 4. Render the Screen
DefaultCurrencyScreen(
availableCurrencies = viewModel.availableCurrencies,
selectedCurrencyCode = selectedCurrency,
onCurrencySelected = { newCurrencyCode ->
viewModel.onCurrencySelected(newCurrencyCode)
// Handle navigation side-effect
scope.launch {
delay(UiConstants.NAV_FEEDBACK_DELAY)
navController.popBackStack()
}
}
)
}
}
- Never pass a
ViewModelinto aScreen. - Never pass a
NavControllerinto aScreen. - Pass them into the
Feature, which orchestrates theScreen.