Imagine a composable A that haves a lower composable B that haves a even lower composable C with haves some buttons.
The buttons must change a state remember variable stored in composable A. The state hoist theory tells you that you must pass, for each button, the variable value and the lambda function that modifies the state variable in two parameters from A to B, then, from B to C. But if there are 10 buttons, you will need to pass a lot of lambdas and parameters, it seems to be overprogramming and very complex.
Is there a better way to achieve this?
This is a sample code similar to that A B C hierarchy, in which the Button composables needs to change the state var currentImageModel in the main composable ImagePortfolio
@Composable
fun ImagePortfolio(modifier: Modifier = Modifier) {
val currentImageModel by remember { mutableStateOf(0) }
Column(
modifier = modifier
.fillMaxSize()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
ImageHolder(
ImageModelProvider.imageModelList[currentImageModel].imageResource,
modifier.weight(0.75f).wrapContentSize()
)
Column(
modifier.weight(0.25f).fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.Bottom),
horizontalAlignment = Alignment.CenterHorizontally
) {
DescriptionHolder(
ImageModelProvider.imageModelList[currentImageModel].imageDescription,
ImageModelProvider.imageModelList[currentImageModel].imageAuthor,
ImageModelProvider.imageModelList[currentImageModel].imageYear.toString()
)
ButtonsHolder()
}
}
}
@Composable
fun ImageHolder(imageResource: Int, modifier: Modifier = Modifier) {
Image(
painter = painterResource(id = imageResource),
contentDescription = null,
modifier = modifier
.border(BorderStroke(16.dp, Color.White))
.padding(16.dp)
.shadow(elevation = 20.dp)
)
}
@Composable
fun DescriptionHolder(description: String, author: String, year: String, modifier: Modifier = Modifier) {
Column (
modifier = modifier
.fillMaxWidth(0.8f)
.background(colorResource(R.color.description_background))
.padding(8.dp)
) {
Text(
text = description
)
Row (
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start)
) {
Text(
text = author,
fontWeight = FontWeight.Bold
)
Text(
text = "($year)"
)
}
}
}
@Composable
fun ButtonsHolder(modifier: Modifier = Modifier) {
Row (
modifier = modifier
.fillMaxWidth(0.9f)
.padding(4.dp),
horizontalArrangement = Arrangement.SpaceBetween
){
Button(R.string.previous, {}, Modifier.width(120.dp))
Button(R.string.next, {}, Modifier.width(120.dp))
}
}
@Composable
fun Button(textResource: Int, function: (()-> Unit), modifier: Modifier = Modifier) {
Button(
modifier = modifier,
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.button_color)),
onClick = function
) {
Text(text = stringResource(id = textResource))
}
}
lambdas functions can trigger recomposition. it's better use a "data class" with "remembers" to avoid it.
actions class contains all event and listeners of screen.
About DescriptionHolder you can use a immutable class
Important use "val" and not "var"
Documentation:
https://developer.android.com/jetpack/compose/performance/stability
and good practices
https://www.youtube.com/watch?v=EVVFhyuVV5g