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.