0

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()
    }
}
wbk727
  • 8,017
  • 12
  • 61
  • 125

2 Answers2

0

(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)

JustSightseeing
  • 1,460
  • 3
  • 17
  • 37
0

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))
        )
    }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • Still not appearing at all for some reason but yet if I put a `Text` box inside the Box it appears. – wbk727 Aug 27 '23 at 14:06
  • This Modifier.drawBehind relies on `size.width` which comes from size modifier applied to Composable or its content measurement. If you don't pass width and height size returns zero in `drawScope`. You can draw anywhere in `drawScope` unless clip is applied even if size is zero but when you need to use it there has to be some dimension applied to composable – Thracian Aug 27 '23 at 14:12
  • PointerInputScope and DrawScope size comes from content or size modifiers. To be sure you can debug what size it returns when it doesn't appear – Thracian Aug 27 '23 at 14:16
  • Very strange. So can something similar to `fillMaxWidth` be used to use the maxium width instead of a specific numerical value? – wbk727 Aug 27 '23 at 14:29
  • You can use any size modifier, i don't mean just Modifier.size, anything that changes min from 0 to another value. It's a Box without content lambda. Modifier.background() or Modifier.border also do not appear if Box doesn't have a size, `Box(modifier = Modifier.height(60.dp).background(Color.Red))`. This is not about Modifier, this is about not having Constraints with minWidth/Height bigger than 0. You can find which size modifier returns which constraints in Constraints section of this answer. https://stackoverflow.com/a/73316247/5457853 – Thracian Aug 28 '23 at 05:10