v79
12/10/2022, 8:48 AMViewModel
object into loads of composable functions, and that feels like a code smell. For instance, if a button has an onClick action which needs to call a viewModel function, but that button is a child of a child of a child....Morgane Soula
12/10/2022, 11:46 AMFunkyMuse
12/10/2022, 11:57 AMv79
12/10/2022, 12:03 PMFunkyMuse
12/10/2022, 12:04 PMyschimke
12/10/2022, 12:25 PMAdel Ayman
12/10/2022, 2:19 PMChris Fillmore
12/10/2022, 3:09 PM@Composable
fun UserCard(
name: String,
photoUri: Uri,
onContactClick: () -> Unit,
onProfileClick: () -> Unit,
) {
Row {
Image(photoUri)
Column {
Text(name)
Row {
Button(onContactClick) { Text("Contact this user") }
Button(onProfileClick) { Text("Open user profile") }
}
}
}
}
This only passes lambdas two levels down (into UserCard and then into Button). But it sounds like in your example youâre passing many more levels down. You could try this instead:
@Composable
fun UserCard(
name: String,
photoUri: Uri,
buttons: @Composable () -> Unit,
) {
Row {
Image(photoUri)
Column {
Text(name)
Row {
buttons()
}
}
}
}
and you use it like this:
UserCard(
name = ...,
photoUri = ...,
buttons = {
Button(
onClick = {
// Handle contact user
},
) { Text("Contact this user") }
Button(
onClick = {
// Handle open user profile
},
) { Text("Open user profile") }
},
)
Now your lambdas are hoisted to the top. And, this way your UserCard
is focused on layout, instead of the details of interactions. If you want, you could wrap the UserCard call site in a container that supplies the viewModel:
UserCardWrapper(
viewModel: MyViewModel = viewModel()
) {
...
}
Filip Wiesner
12/10/2022, 3:29 PMCasey Brooks
12/10/2022, 4:15 PMpostInput
callback
Hereâs a rough idea of what I typically do:
kotlin
object Screen1Contract {
data class State(
val stringValue: String,
val intValue: Int,
)
sealed class Inputs {
data class UpdateStringValue(val newStringValue: String) : Inputs()
data class UpdateIntValue(val newIntValue: String) : Inputs()
}
}
@Composable
fun MainContent() {
var uiState by remember { mutableStateOf(Screen1Contract.State()) }
MainContent(uiState) { input ->
when(input) {
is Screen1Contract.Inputs.UpdateStringValue -> {
uiState = uiState.copy(stringValue = input.newStringValue)
}
is Screen1Contract.Inputs.UpdateIntValue -> {
uiState = uiState.copy(intValue = input.newIntValue)
}
}
}
}
@Composable
fun MainContent(
uiState: Screen1Contract.State,
postInput: (Screen1Contract.Inputs)->Unit,
) {
// ...
}
Casey Brooks
12/10/2022, 4:16 PM