Declarative UI: state flows down, events flow up. Small, stable and predictable composables. App architecture and DI live in android-development; MVVM in mvvm-architecture. Visual style: better-mobile-interface. Strings: i18n-mobile.
Principles
- Composable = state → UI function. No side effects in the body without
LaunchedEffect/SideEffect/etc. - State as low as possible; hoist only to share.
- Unidirectional data flow:
state+onEvent/ lambdas. - Hoisting: dumb components receive values and callbacks.
- Stability matters for recomposition; avoid unnecessary unstable types in hot params.
State
| API | Usage |
|---|---|
remember |
Value caching in composition |
rememberSaveable |
Survives recreation (bundle) |
mutableStateOf / MutableState |
Local observable state |
collectAsStateWithLifecycle |
Flow/StateFlow with lifecycle |
ViewModel + StateFlow |
Screen state |
- Do not use
ViewModelfor ephemeral widget state (local expand/collapse). - Avoid
LiveData.observeAsStatein new code if Flow is available. - Derived state:
derivedStateOffor expensive/filtered derived calculations.
Side effects
LaunchedEffect(key)— coroutines tied to the composition lifecycle.DisposableEffect— register/unregister with cleanup.SideEffect— publish to non-Compose APIs.rememberCoroutineScope— actions triggered by UI event.- Never do network/IO directly in the body of
@Composable.
Layout and Material
- Prefer Material 3 (
MaterialTheme) aligned with the app's design system. Modifierin the correct order (size/padding/clickable/semantics).- Slot APIs (
topBar,content) instead of explosive boolean parameters. - Theming: colorScheme, typography, shapes — no magic colors scattered around.
- Dark theme and dynamic color only if the product already adopts them.
Lists and performance
LazyColumn/LazyRow/LazyGridwithkeystable.- Avoid unnecessary unstable lambdas and new objects in hot items.
contentTypewhen it helps recycling.- Images: project lib (Coil/Glide Compose) with adequate size.
- Measure recompositions (Layout Inspector / Compose metrics) before micro-optimizing.
Compose Navigation
- Navigation Compose with typed routes (type-safe routes if the project uses them).
- Serializable args and safe defaults.
- Correct
ViewModelscopes (navigationback stack entry). - Deep links integrated into the graph.
- Avoid multiple
NavControllerwithout a clear hierarchy.
Views Interop
AndroidView/AndroidViewBindingfor occasional legacy Views.ComposeViewin Fragments/XML Activities.- Do not mix two state systems on the same screen without a clear boundary.
Accessibility
contentDescription,semantics, roles and merge when necessary.- Minimum touch targets (~48dp).
- Scalable font; layouts that do not clip at high fontScale.
- Coherent focus order and announcements.
Previews and tooling
@Previewwith states: loading, empty, error, content.- Previews with light/dark theme and relevant screen sizes.
- Extract fake preview data; do not depend on real Hilt in the preview.
Anti-patterns
- God Composable with network, DB, and navigation
GlobalScope/ coroutines without cancellation- Duplicated state (ViewModel + remember of the same field)
Modifieromitted in public components (makes layout harder)- Recomposition caused by
List/Mapunstable values recreated on every emit - Ignore configuration changes in critical state
Completion criteria
- Clear UDF; side effects only in effect APIs
- Lists with keys; themes via
MaterialTheme - Basic A11y in the feature
- Previews of the main states
- No IO in the Composable body
- Integration with the project's ViewModel/DI preserved