Movies UI - Lists
Wrapup
Now we have some nice list support in our application.
Note that some of our functions are gathering quite a few parameters. There are a few ways to simplify this that we won't be digging into in this class.
Create a State holder
You can group multiple parameters using the "Parameter Object" pattern, into a "State holder". This is a class that is @Stable
or @Immutable
and contains multiple state and event functions to simplify parameter passing.
The "Parameter Object" pattern also helps reduce changes needed when parameters are added or removed.
As a quick example, suppose we had a chain of composables that use a custom scaffold we've created. The custom Scaffold takes a list of Action
objects that describe top-bar actions.
class Action(
icon: ImageVector,
description: String,
onClick: () -> Unit
)
@Composable
fun PersonListScreen(
title: String,
actions: List<Action>,
personList: List<Person>,
...
) {
ListScaffold(title, actions, personList, ...)
}
@Composable
fun <T> ListScaffold(
title: String,
actions: List<Action>,
items: List<T>,
...
) {
CustomScaffold(title, actions) {
// display items in a LazyColumn
}
}
@Composable
fun CustomScaffold(
title: String,
actions: List<Action>,
content: @Composable (PaddingValues) -> Unit
) {
Scaffold(
topBar = { /* set up title and actions */ }
...
content = content,
)
}
Note how we need to pass mutiple parameters down just to get them to the CustomScaffold
.
Think about what happens when we need to add or remove another parameter for CustomScaffold
; we'd need to pass it down or remove it as well.
By grouping parameters into a Parameter object, we can alleviate this:
class Action(
icon: ImageVector,
description: String,
onClick: () -> Unit
)
class CustomScaffoldState(
title: String,
actions: List<Action>,
)
@Composable
fun PersonListScreen(
scaffoldState: CustomScaffoldState,
personList: List<Person>,
...
) {
ListScaffold(scaffoldState, personList, ...)
}
@Composable
fun <T> ListScaffold(
scaffoldState: CustomScaffoldState,
items: List<T>,
...
) {
CustomScaffold(scaffoldState) {
// display items in a LazyColumn
}
}
@Composable
fun CustomScaffold(
scaffoldState: CustomScaffoldState,
content: @Composable (PaddingValues) -> Unit
) {
Scaffold(
topBar = { /* set up title and actions from scaffoldState */ }
...
content = content,
)
}
Now we can add/remove parameters to/from CustomScaffoldState
without affecting the intermediate functions.
CustomScaffoldState
is known as a "State holder" in Compose terms.
Composition Locals
Alternatively, you can define a Composition Local to scope data around composable calls.
Warning
I feel this is generally a bad idea, as it makes composable functions more difficult to reason about and test. In general, passing parameters down makes your composable functions much more clear.
There are some uses of it in Compose, such as MaterialTheme
, LocalContext
and LocalDensity
that are useful, but I would much rather have had a context object that all functions take that accesses these data.
The above link gives a great description of Composition Locals, but please don't use them, or use them only with great responsibility.