Hinaka
06/18/2024, 5:05 AM@Composable
fun Layout(
label: String,
component: @Composable (label: String, enabled: Boolean) -> Unit,
modifier: Modifier = Modifier,
) { ... }
@Composable
fun Layout(
label: String,
optionalIcon: ImageVector,
component: @Composable (label: String, enabled: Boolean, icon: ImageVector?) -> Unit,
modifier: Modifier = Modifier,
) { ... }
@Composable
fun Layout(
label: String,
optionalLeadingIcon: ImageVector,
optionalTrailingIcon: ImageVector,
component: @Composable (label: String, enabled: Boolean, leadingIcon: ImageVector?, trailingIcon: ImageVector?) -> Unit,
modifier: Modifier = Modifier,
) { ... }
The idea is that component
will be based on other arguments.
Currently this working find for me, but as I add more and more argument, I have to create more and more composable function for it.
Is there a better way to write this, I'm looking at extensible data arguments but seem likes it not suitable.Lukas Anda
06/18/2024, 7:56 AM_
to not need to use the variableHinaka
06/18/2024, 8:35 AMLayout(
label = "Chip",
optionalIcon = Icons.Filled.Check,
component = { label, enabled, optionalIcon ->
// chip implementation with label and icon
}
)
and this is how it will look like if I use default values arguments:
Layout(
label = "Chip"
component = { label, enabled, _, optionalIcon, _, _, _, _, _, _, _ ->
// chip implementation with label and icon
}
)
Lukas Anda
06/18/2024, 8:46 AMLukas Anda
06/18/2024, 8:46 AMMatthew Feinberg
06/25/2024, 11:06 PMcomponent: @Composable ComponentContext.() -> Unit
Then have an appropriate interface...
interface ComponentContext {
val label: String
val enabled: Boolean
val leadingIcon: ImageVector?
val trailingIcon: ImageVector?
}
Alternative, you could just pass it to the lambda:
component: @Composable (ComponentContext) -> Unit
Using a receiver, the syntax of the lambda is the same (except you don't need to give the argument names):
component = {
Text(label)
}
...but you do have to worry about namespace conflicts (if you later add a val splunge: String
but already have code using splunge
for something else...)
Passing it as an argument, it's a little more verbose, but you avoid namespace conflicts:
component = {
Text(it.label)
}
Of course, there's an object allocation this way (vs. just passing everything as individual lambda arguments). In most cases, unless you're doing some really heavy recomposition, I don't think that will be an issue. If it does become an issue, you can always reuse the ComponentContext
instance... (with appropriate cautions about the lambda not keeping a reference...)
If you take this approach, another thing to be careful about is how you handle State
. If you do it wrong, you can end up recomposing unnecessarily or recomposing too often. For example, if you naively make ComponentContext
as a data class and reconstruct it every time, then you'll be referencing the state during the construction, which is probably not what you want.
Something like...
object : ComponentContext {
override val label: String get() = labelState.value
}
...or something like that is probably best.
It can be a lot of extra work to set it up, but if you're building a common component that's going to re-used in a lot of places, it might be worth it.Hinaka
06/26/2024, 12:03 PM@Composable
fun Layout(
label: String,
component: @Composable (label: String, enabled: Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
Content(
component = { state ->
component(state.label, state.enabled)
}
)
}
@Composable
fun Layout(
label: String,
optionalIcon: ImageVector,
component: @Composable (label: String, enabled: Boolean, icon: ImageVector?) -> Unit,
modifier: Modifier = Modifier,
) {
Content(
component = { state ->
component(state.label, state.enabled, state.icon)
}
)
}
@Composable
private fun Content(
component: @Composable (state: ComponentState) -> Unit,
) { ... }
so that I can use the public component like this:
Layout(
label = "Chip",
optionalIcon = Icons.Filled.Check,
component = { label, enabled, optionalIcon ->
// chip implementation with label and icon
}
)
Hinaka
06/26/2024, 12:06 PMContent
.
• The public component lambda only exposes what is necessary for its usage, eliminating the need to use _
for unused variables.