How to create multicoloured box divided equally in Jetpack Compose

I’m trying to show a horizontal box divided into multiple equals parts containing different colours. How can I use the specified integer to divide the box, then use the list of specified colours to colour the sections in?

Expected result

enter image description here

Current reuslt

enter image description here

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NestedScrollingTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    MyScreen()
                }
            }
        }
    }
}

@Composable
fun MyReusableScaffold(scaffoldTitle: String, scaffoldFab: @Composable () -> Unit,
                       scaffoldContent: @Composable (contentPadding: PaddingValues) -> Unit) {
    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()

    Scaffold(
        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
        topBar = {
            LargeTopAppBar(
                title = { Text(text = scaffoldTitle) },
                scrollBehavior = scrollBehavior
            )
        },
        floatingActionButton = { scaffoldFab },
        content = { contentPadding -> scaffoldContent(contentPadding = contentPadding) }
    )
}

@Composable
fun ReusableRow(rowItems: Array<RowItems>) {
    val scrollState = rememberScrollState()

    Row(modifier = Modifier
        .height(300.dp)
        .horizontalScroll(state = scrollState),
        verticalAlignment = Alignment.CenterVertically) {
            rowItems.forEach { rowItem ->
                MyInfoCard(cardTitle = rowItem.rowItemTitle, cardSubtitle = rowItem.rowItemDesc)
            }
    }
}

@Composable
fun <T> ReusableLazyColumn(
    header: @Composable () -> Unit,
    lazyColumnItems: Array<T>,
    lazyColumnItemLayout: @Composable Any.(item: T) -> Unit,
    onClickLazyColumnItem: (T) -> Unit
) {
    val listState = rememberLazyListState()

    Box(modifier = Modifier.fillMaxSize()) {
        LazyColumn(
            state = listState,
            contentPadding = PaddingValues(top = 0.dp, bottom = 80.dp)
        ) {
            item { header.invoke() }
            item { Spacer(modifier = Modifier.height(24.dp)) }
            items(lazyColumnItems) { itemC ->
                Row(modifier = Modifier
                    .fillMaxWidth()
                    .clickable {
                        onClickLazyColumnItem(itemC)
                    }) {
                    lazyColumnItemLayout(itemC)
                }

                Divider(color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f))
            }
        }

        ButtonScrollToTopList(listState = listState)
    }
}

@Composable
fun MyScreen() {
    MyReusableScaffold(
        scaffoldTitle = "Demo",
        scaffoldFab = { /*TODO*/ },
        scaffoldContent = {
            MyScreenContent(contentPadding = it)
        }
    )
}

@Composable
fun MyScreenContent(
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues()
) {
    val rowItems = arrayOf(
        RowItems.ItemRowNeapolitan,
        RowItems.ItemRowSpumoni
    )

    val columnItems = arrayOf(
        ColumnItems.ItemPetitFour,
        ...
        ColumnItems.ItemUpsideDownCake
    )

    Box(modifier = modifier
        .fillMaxSize()
        .padding(contentPadding)
    ) {
        Column(content = {
                ReusableLazyColumn(
                    header = { ReusableRow(rowItems) },
                    lazyColumnItems = columnItems,
                    lazyColumnItemLayout = {
                        Row { ... }
                    },

                    onClickLazyColumnItem = { listenerColumnItems }
                )
            }
        )
    }
}

enum class RowItems(val rowItemTitle: String, val rowItemDesc: String,
                    val barSectionCount: Int, val barSectionColors: List<Any>) {
    ItemRowNeapolitan("Neapolitan ice cream",
        "vanilla, chocolate, and strawberry",
        3, 
        listOf(0xFFFBFFFE, 0xFF774B3B, 0xFFEFAD89)
    ),
    ItemRowSpumoni("Spumoni ice cream",
        "vanilla, pistachio, and strawberry",
        3,
        listOf(0xFFFBFFFE, 0xFF93C572, 0xFFEFAD89)
    )
}

enum class ColumnItems(val columnItemTitle: String) {
    ItemPetitFour("Petit Four"),
    ...
    ItemUpsideDownCake("Upside Down Cake")
}

val listenerColumnItems: (ColumnItems) -> Unit = {...}

@Composable
fun MyInfoCard(
    cardTitle: String,
    cardSubtitle: String,
//    boxSections: Int,
//    boxSectionColours: Array<Any>
) {
    OutlinedCard(modifier = Modifier
        .height(350.dp)
        .padding(16.dp),
        shape = RoundedCornerShape(size = 16.dp),
    ) {
        Column() {
            Box(
                modifier = Modifier
                    .background(???)
                    .fillMaxWidth()
                    .height(30.dp)
            )
            Text(text = cardTitle)
            Text(text = cardSubtitle)
        }
    }
}

val listenerRowItems: (RowItems) -> Unit = { item ->
    when (item) {
        RowItems.ItemRowCheetah -> TODO()
        ...
        RowItems.ItemRowSonoma -> TODO()
    }
}

enum class ColumnItems(val columnItemTitle: String) {
    ItemPetitFour("Petit Four"),
    ...
    ItemUpsideDownCake("Upside Down Cake")
}


val listenerColumnItems: (ColumnItems) -> Unit = { item ->
    when (item) {
        ColumnItems.ItemPetitFour -> TODO()
        ...
        ColumnItems.ItemUpsideDownCake -> TODO()
    }
}

(Typing this on a phone, so some stuff might be wrong, will factcheck later <3)

a simple composable like:

@Composable
fun Stackoverflow() {
    val listOfColors = listOf(Color.Gray, Color.Black, Color.Red)
    Row {
        listOfColors.forEach {
            Box(modifier = Modifier.height(50.dp).weight(1f).background(it))
        }
    }
}

should do the job (the .weight(1f) makes sure all of the boxes take the same amount of space)

This can also be done as as Modifier that draws multi-colored background

fun Modifier.multiColorBackground(
    colors: List<Color>
) = this.then(
    Modifier.drawBehind {
        if (colors.isNotEmpty()) {
            val colorCount = colors.size
            val width = size.width / colorCount
            colors.forEachIndexed { index, color ->
                drawRect(
                    color = color,
                    topLeft = Offset(index * width, 0f),
                    size = Size(width, size.height)
                )
            }
        }
    }
)

Result

enter image description here

Demo

@Preview
@Composable
fun ColorTest() {
    Column(
        modifier = Modifier
            .background(Color.Black)
            .padding(top=20.dp)
            .fillMaxSize()
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(60.dp)
                .multiColorBackground(listOf(Color.White, Brown400, Pink400))
        )
    }
}

Leave a Comment