julioromano
06/24/2021, 1:19 PM@Composable APIs, is it preferable to have a default onClick param as an empty lambda (onClick: () -> Unit = {}) or as a nullable function (onclick: (() -> Unit)? = null )? Are there any pros and cons?albertosh
06/24/2021, 1:27 PMBryan Herbst
06/24/2021, 1:28 PMonClick as a default seems strange- it implies that your Composable requires handling click behavior, but offers doing nothing as a default. If I think about this in the context of a Button for example, I expect clicking the button would produce a ripple or other indication when I tap it, but that just wouldn’t do anything. That doesn’t sound like expected behavior.
On the other hand, making the onClick nullable implies that handling clicks is optional. A good example of this type of API design decision in action is Checkbox, which offers a nullable onCheckedChange. Checkbox only adds the selectable() Modifier when this parameter is not null, so it doesn’t handle clicks at all when onCheckedChange is null.
Short version: your choice here implies behavior for the component, and you should consider what correct behavior for your component is, including what’s required vs. what’s not.julioromano
06/24/2021, 1:54 PMonClick: () -> Unit = {} => onClick is required
2. onclick: (() -> Unit)? = null => onClick is optional
But since in case 1 onClick is required, why having a default value at all? Doesn’t the = {} add confusion?
It seems sort of… Yeah this parameter is required but you don’t have to pass it in…Albert Chang
06/24/2021, 2:06 PMAlexandre Elias [G]
06/25/2021, 1:18 AMAlexandre Elias [G]
06/25/2021, 1:33 AMAlexandre Elias [G]
06/25/2021, 1:37 AMval clickable = if (onClick != null) { Modifier.clickable(...) } else { Modifier } ... `modifier.then(clickable)`(as opposed to the shorter way modifier.clickable(onClick ?: {}, ...) ). That way, not only will you get a slight performance improvement, but more importantly it will send the right signals to accessibilityjulioromano
06/25/2021, 8:44 AMSurface in beta09: it’s got two different @Composables with either no onClick at all or a non-null one.
Since I needed an optional onClick in my custom Composable (which uses Surface internally) I had to use the version of Surface without onClick and then place a Box inside it and apply the onClick modifier to the Box:
modifier = Modifier
.fillMaxSize()
.run {
if (onClick == null) this
else clickable(onClick = onClick)
}Alexandre Elias [G]
06/25/2021, 5:12 PMButton did it this way. However, we found a problem with this approach: the strong Compose convention is to put passed-in modifier parameter on the outermost Layout, but given that the Box is inside the Surface, either this convention must be violated, or a testTag that's passed in will appear not clickableAlexandre Elias [G]
06/25/2021, 5:17 PMSurface itself and instead directly chain the elevation, shadow, clip modifiers, B) conditionally use one Surface overload or the other by storing the "content" lambda in a val instead of following the usual concise last-lambda styleAlexandre Elias [G]
06/25/2021, 5:42 PMjulioromano
06/28/2021, 11:39 AM