Pascal Ganaye
Pascal Ganaye

Reputation: 1364

Jetpack compose hierarchy awareness

I created a couple of Chip and Chips @Composable functions that I use like this:

    Chips {
        Chip(text = "Apple")
        Chip(text = "Banana")
        Chip(text = "Cherry")
    }

I'd like the first and last chip to look a bit different. I could do it by using extra arguments to the Chip function:

    Chips {
        Chip(text = "Apple", isFirst = true)
        Chip(text = "Banana")
        Chip(text = "Cherry", isLast = true)
    }

It is a bit cumbersome. How can I make an individual Chip aware of its siblings.

Similarly we might want to change the color of the Chips depending on whether there are Chips inside or not.

    Chips(chipCount = if (addCherry) 3 else 2) {
        Chip(text = "Apple")
        Chip(text = "Banana")
        if (addCherry) Chip(text = "Cherry")    
    }

How would I do that more elegantly.

Upvotes: 1

Views: 606

Answers (2)

Primož Ivančič
Primož Ivančič

Reputation: 2376

I would send array of chip data into Chips composable and then use a loop to add Chip composables.

@Composable
fun Chips(
    chips: List<ChipData>
) {
    chips.forEachIndexed { index, chip ->
        Chip(
            text = chip.text,
            isFirst = index == 0,
            isLast = index == chips.lastIndex,
        )
    }
}

Similarly, you can change the colour of Chips based on the size of the list of Chip objects.

If you want to pass multiple completely different composables, slotting might as well save you (this is how toolbars and such are done). Instead of an object, you send in a composable, which can then be anything you want (the same was a Button is also done). Here is an example of it:

@Composable
fun ListOfComposables() {
    val comps = listOf<@Composable (isFirst: Boolean, isLast: Boolean) -> Unit>(
        { isFirst, isLast -> Chip(text = "first chip", isFirst = isFirst, isLast = isLast) },
        { isFirst, isLast -> Chip(text = "chip2", isFirst = isFirst, isLast = isLast) },
        { _, _ ->
            Button(onClick = { }) {
                Text(text = "Button")
            }
        },
        { isFirst, isLast -> Chip(text = "Chip3", isFirst = isFirst, isLast = isLast) },
        { _, _ -> Text(text = "Random text") },
        { isFirst, isLast -> Chip(text = "last chip", isFirst = isFirst, isLast = isLast) }
    )

    Column {
        comps.forEachIndexed { index, function ->
            function(
                isFirst = index == 0,
                isLast = index == comps.lastIndex
            )
        }
    }
}

@Composable
fun Chip(
    text: String,
    isFirst: Boolean = false,
    isLast: Boolean = false
) {
    Text(
        text = text,
        color = when {
            isFirst -> Color.Green
            isLast -> Color.Red
            else -> Color.Unspecified
        },
        modifier = Modifier
            .background(Color.LightGray),
    )
}

The preview of this code looks like this: preview.

Code is quite ugly, but it gets the point across, I hope.

Upvotes: 2

Kuruchy
Kuruchy

Reputation: 1370

I would pass the list in the Chips composable, but I will add to the ChipData the uiInfo (a sealed Class or an enum) and then inside the forEach loop I would do a when.

Something like this:

    enum class UIInfo {
        FILTER, INVERSE, NORMAL
    }

    data class ChipData(
        val text: String,
        val uiInfo: UIInfo,
    )

    @Composable
    fun Chips(
        chips: List<ChipData>
    ) {
        chips.forEach { chip ->
            Chip(
                text = chip.text,
                uiInfo = chip.uiInfo,
            )
        }
    }

Upvotes: 0

Related Questions