Pferdesalbe
Pferdesalbe

Reputation: 213

Jetpack Compose - layouting reusable components

for practicing with reusable components in Jetpack Compose, I started a little exercise. See picture below.

enter image description here

As I imagine the green row, the input row, and the rows between have the same construction. The first element got the available space, the second takes 50.dp, and the last one got 70.dp. I tried to seperate the width into variables an pass this vars as modifiers to the single elements in the row. I thought if I need additionally fields, the I can extend it whitout any problem.

CODE DOESN'T WORK!

@Composable
fun groundComponent(
    modifier: Modifier = Modifier,
    spaceBetween: Dp = 0.dp,
    color: Color,
    content: @Composable () -> Unit
) {
    Surface(
        color = color
    ) {
        Row(
            modifier = modifier,
            horizontalArrangement = Arrangement.spacedBy(spaceBetween)
        ) {
            content()
        }
    }
}

@Composable
fun inputSection() {
val firstRowWidth = 1F
val secondRowWidth = 70.dp
val thirdRowWidth = 50.dp

Text("Add Ingredient")
groundComponent(color = Color.Green){
            Text( text="Ingredient", modifier = Modifier.weight(firstRowWidth ))
            Text( text="Amount", modifier = Modifier.widthIn(secondRowWidth ))
            Text( text="Unit", modifier = Modifier.widthIn(thirdRowWidth ))
        }
groundComponent{
            Text( text="Sugar", modifier = Modifier.weight(firstRowWidth ))
            Text( text="500", modifier = Modifier.widthIn(secondRowWidth ))
            Text( text="gr", modifier = Modifier.widthIn(thirdRowWidth ))
        }
groundComponent{
            Text( text="Carrot", modifier = Modifier.weight(firstRowWidth ))
            Text( text="1.5", modifier = Modifier.widthIn(secondRowWidth ))
            Text( text="kg", modifier = Modifier.widthIn(thirdRowWidth ))
        }
groundComponent{
                TextField(
value = "newIngredient", 
onValueChange = {}, 
modifier = Modifier.weight(firstRowWidth ))
            TextField(
value = "newAmount", 
onValueChange = {}, 
modifier = Modifier.widthIn(secondRowWidth )
)
            TextField(
value = "newUnit", 
onValueChange = {}, 
modifier = Modifier.widthIn(thirdRowWidth )
)
        }
Button(onClick={}){Text("add")}
}

I got several errors with the .weight modifier. So how is the right aproach to solve such a situation.

Thanks!

Upvotes: 2

Views: 994

Answers (2)

Pferdesalbe
Pferdesalbe

Reputation: 213

Thanks for your reply and your pretty good explanation! With your help I solved my problem this way.

@Composable
fun InputRowGroundComponent(
    modifier: Modifier = Modifier,
    spaceBetweenElements: Dp = 0.dp,
    color: Color,
    content: @Composable RowScope.() -> Unit
) {
    Surface(
        color = color
    ) {
        Row(
            modifier = modifier,
            horizontalArrangement = Arrangement.spacedBy(spaceBetweenElements),
            verticalAlignment = Alignment.CenterVertically
        ) {
            content()
        }
    }
}

@Composable
fun OverviewHeader(
    modifier: Modifier = Modifier,
    text: String
) {
    Text(
        modifier = modifier,
        text = text,
        maxLines = 1,
        overflow = TextOverflow.Ellipsis,
        textAlign = TextAlign.Center
    )
}

@Composable
fun OverviewContent(
    modifier: Modifier = Modifier,
    text: String
) {
    Text(
        modifier = modifier,
        text = text,
        maxLines = 2,
        overflow = TextOverflow.Ellipsis,
    )
}

@Preview(showBackground = true, widthDp = 460)
@Composable
fun testPrev() {
    val rowWeights = listOf(6F,3F,2F)
    val rowSpacing = 8.dp
    val indentation = 10.dp
    Column(
        modifier = Modifier.padding(8.dp),
        verticalArrangement = Arrangement.spacedBy(rowSpacing)
    ) {
        InputRowGroundComponent(
            modifier = Modifier.heightIn(45.dp),
            spaceBetweenElements = rowSpacing,
            color = Color.Green
        ) {
            OverviewHeader(text = "Ingredient", modifier = Modifier.weight(rowWeights[0]))
            OverviewHeader(text = "Amount", modifier = Modifier.weight(rowWeights[1]))
            OverviewHeader(text = "Unit", modifier = Modifier.weight(rowWeights[2]))
        }
        InputRowGroundComponent(
            modifier = Modifier.heightIn(30.dp),
            spaceBetweenElements = rowSpacing,
            color = Color.Unspecified
        ) {
            OverviewContent(text = "Sugar", modifier = Modifier.weight(rowWeights[0]).padding(start=indentation))
            OverviewContent(text = "500", modifier = Modifier.weight(rowWeights[1]).padding(start=indentation))
            OverviewContent(text = "gr", modifier = Modifier.weight(rowWeights[2]).padding(start=indentation))
        }
        InputRowGroundComponent(
            modifier = Modifier.heightIn(30.dp),
            spaceBetweenElements = rowSpacing,
            color = Color.Unspecified
        ) {
            OverviewContent(text = "Carrot", modifier = Modifier.weight(rowWeights[0]).padding(start=indentation))
            OverviewContent(text = "1.5", modifier = Modifier.weight(rowWeights[1]).padding(start=indentation))
            OverviewContent(text = "kg", modifier = Modifier.weight(rowWeights[2]).padding(start=indentation))
        }
        InputRowGroundComponent(
            spaceBetweenElements = rowSpacing,
            color = Color.Unspecified
        ) {
            TextField(value = "", onValueChange = {}, modifier = Modifier.weight(rowWeights[0]))
            TextField(value = "", onValueChange = {}, modifier = Modifier.weight(rowWeights[1]))
            TextField(value = "", onValueChange = {}, modifier = Modifier.weight(rowWeights[2]))
        }
        Button(
            modifier = Modifier.fillMaxWidth(),
            onClick = { /*Todo*/ },
            content = {
                Row(
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Icon(
                        imageVector = Icons.Filled.Add,
                        contentDescription = "Add Ingredient"
                    )
                    Text(
                        text = "Add"
                    )
                }
            }
        )
    }
}

Is this approach now right?

Upvotes: 1

Thracian
Thracian

Reputation: 66516

Modifier.weight is a Modifier that defined in specific scopes such as RowScope and ColumnScope. To be able to use modifiers that are defined in specific scopes you need to add Receiver to your content. BoxScope as Modifier.align() that is defined for instance, you can define your scopes either.

@Composable
fun GroundComponent(
    modifier: Modifier = Modifier,
    spaceBetween: Dp = 0.dp,
    color: Color=Color.Unspecified,
    content: @Composable RowScope.() -> Unit
) {
    Surface(
        color = color
    ) {

        // Can't call content here because it has RowScope as receiver
//        content()
        Row(
            modifier = modifier,
            horizontalArrangement = Arrangement.spacedBy(spaceBetween)
        ) {
            content()
        }
    }
}

Also in InputSection you define weight fractions as

val firstRowWidth = 1F
val secondRowWidth = 70.dp
val thirdRowWidth = 50.dp

these values should be proportionate to each other

if you set 1/5/6 for instance. or between 0f-1f

And by convention you can name Composable with capital initial letter since they are considered as widgets.

Upvotes: 5

Related Questions