You built screens in the projects. This lesson fills the gaps you need for polished, real apps: app structure, material components, theming, side effects, animation and accessibility.
App structure with Scaffold
Scaffold gives a standard Material 3 page with slots for a top bar, bottom bar, floating action button and snackbar host. Your content receives innerPadding, which you must apply so it is not hidden behind bars.
Scaffold(
topBar = { TopAppBar(title = { Text("Home") }) },
bottomBar = {
NavigationBar {
NavigationBarItem(selected = true, onClick = { }, icon = { Icon(Icons.Default.Home, null) }, label = { Text("Home") })
NavigationBarItem(selected = false, onClick = { }, icon = { Icon(Icons.Default.Search, null) }, label = { Text("Search") })
}
},
floatingActionButton = { FloatingActionButton(onClick = { }) { Icon(Icons.Default.Add, "Add") } },
snackbarHost = { SnackbarHost(snackbarHostState) }
) { innerPadding ->
Content(Modifier.padding(innerPadding))
}
For a navigation drawer, wrap the screen in ModalNavigationDrawer and open it with drawerState.open() inside a coroutine. Show a snackbar with snackbarHostState.showSnackbar("Saved") from a coroutine. An AlertDialog is just a composable you include when a flag is true.
Lists and grids
LazyColumnandLazyRowfor vertical and horizontal lists.LazyVerticalGrid(columns = GridCells.Fixed(2))orGridCells.Adaptive(150.dp)for grids.items(list, key = { it.id })andstickyHeaderfor section headers.rememberLazyListState()to read or control the scroll position.
Theming
MaterialTheme carries a colour scheme, typography and shapes. You define light and dark colour schemes (or use dynamic colour on Android 12 and later) and read values with MaterialTheme.colorScheme.primary. Never hard-code colours in screens: change the theme once and the whole app follows.
Side effects
A composable can run many times, so work with outside effects must go in special blocks.
LaunchedEffect(key): run a suspend block when the composable enters and when the key changes. Used to load data or collect events.DisposableEffect(key): register something and clean it up inonDispose, such as a lifecycle observer or a sensor listener.rememberCoroutineScope(): a scope for launching work from a click handler.SideEffect: publish Compose state to non-Compose code after each successful composition.derivedStateOf { ... }: a value calculated from other state that only changes when the result changes, for exampleshowButton = listState.firstVisibleItemIndex > 0.
val listState = rememberLazyListState()
val showUp by remember { derivedStateOf { listState.firstVisibleItemIndex > 3 } }
LaunchedEffect(userId) { // runs again only when userId changes
viewModel.load(userId)
}
Animation
AnimatedVisibility fades or slides a composable in and out. animateColorAsState, animateDpAsState and animateFloatAsState move a value smoothly to a new target. AnimatedContent animates between two versions of content. Crossfade blends screens. For custom drawing use Canvas. Keep animations short, and respect the user's reduced-motion setting.
val size by animateDpAsState(if (expanded) 200.dp else 80.dp, label = "size")
AnimatedVisibility(visible = showDetails) { DetailsCard() }
Performance and recomposition
- Give list items stable keys.
- Pass small stable values or immutable data classes into composables.
- Read state as late as possible (inside the lambda that needs it).
- Use the Layout Inspector's recomposition counts to find screens that redraw too often.
Responsive layouts and accessibility
Use WindowSizeClass to choose a layout for phones, foldables and tablets, and Modifier.weight and fillMaxWidth instead of fixed sizes. For accessibility, give every icon a contentDescription (or null if it is decorative), keep touch targets at least 48 dp, make sure text contrast is high, support large font sizes, and group related items with Modifier.semantics. Test with TalkBack.
Your task
- Wrap one of your projects in a Scaffold with a top bar and a snackbar that confirms a save.
- Add a scroll-to-top button using derivedStateOf.
- Switch your app to dark theme and fix any hard-coded colours.
- Turn on TalkBack and complete one task in your app.
How to check it worked
- Content is not hidden behind bars.
- Dark mode looks right everywhere.
- You can explain when to use LaunchedEffect and DisposableEffect.