0

Is it possible to do weights in Jetpack Compose with lazy column? I'd like to set it menu item is weighted as 1/n (n = number of menus) of a layout, and the other takes up the remaining 1/n, also.

I want to list it at the same height as the number of menus.

MenuList

@Composable
fun MenuList(
    loading: Boolean,
    menus: List<Menu>,
    onNavigateToMenuDetailScreen: (String) -> Unit
) {
    Box(modifier = Modifier
        .background(color = MaterialTheme.colors.surface)
        .fillMaxSize()) {
        Column(modifier = Modifier.fillMaxSize()) {
            if (loading && menus.isEmpty()) {
                LoadingShimmer(imageHeight = 800.dp)
            }
            else if (menus.isEmpty()) {
                NothingHere()
            }
            else {
                LazyColumn(
                    modifier = Modifier
                        .fillMaxSize()
                        .weight(1F)
                ) {
                    itemsIndexed(
                        items = menus
                    ) { index, menu ->
                        MenuCard(
                            menu = menu,
                            onClick = {
                                
                            }
                        )
                    }
                }
            }
        }
    }
}

MenuCard

@Composable
fun MenuCard(
    menu: Menu,
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
) {
    Card(
        shape = MaterialTheme.shapes.small,
        modifier = Modifier
            .padding(
                bottom = 6.dp,
                top = 6.dp
            )
            .fillMaxWidth()
            .clickable(onClick = onClick),
        elevation = 8.dp
    ) {
        Column {
            Text(
                text = menu.name,
                fontSize = 30.sp,
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentWidth(Alignment.CenterHorizontally)
                    .wrapContentHeight(Alignment.CenterVertically),
                style = MaterialTheme.typography.h3
            )
        }
    }
}

In summary, MenuCards are created as many as the number of menu on the MenuList screen, and I hope the height of each MenuCard will be 1/n. (n = number of menu)

Like, when number of menu is 8, enter image description here

Just same height to each menu.

Polaris Nation
  • 1,085
  • 2
  • 18
  • 49

2 Answers2

0

If all you want is a way to divide the screen height in equal proportions to all the n items, you can simply go for a column.

val items = ... //I assume you have access to this
val screenHeight = LocalConfiguration.current.screenHeightDp
Column{
 items.forEach{
  MenuCard(modifier = Modifier.height(1f / items.size * screenHeight))
 }
}

Column and LazyColumn are fundamentally different. LazyColumn's sole purpose is to deal with large datasets by loading and caching only a small window off that set. Now, if there is nothing to be scrolled, LazyColumn is useless, in fact, it is worse.

EDIT:

You could use BoxWithConstraints to get the screen dimensions as well, but it, in this context, would be more of a workaround than a solution. It adds an extra Composable and decreases the readability of the code too, so for getting the screen dimensions, always use the API that was specifically built for the job.

Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
  • It won't work because fillMaxHeight() will change after every item is added. – Rafiul Nov 08 '21 at 09:45
  • It makes sense to me that it should reduce because you are adding items into a column and every time divided by the same number items.size where available height is decreasing every time. I tested your code and I got this. https://i.imgur.com/2W1f7UK.png . Anyway, I might be wrong. – Rafiul Nov 08 '21 at 10:17
0

Replace your LazyColumn code with BoxWithConstraints and regular Column.

BoxWithConstraints for getting the minHeight or in this case you can say screen height.

Change to something like the below.

BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
    val height = minHeight/menus.size
    Column(
        modifier = Modifier
            .fillMaxSize()
    ) {
        menus.forEachIndexed { index, menu ->
            MenuCard(
                modifier = Modifier.height(height),
                menu = menu,
                onClick = {

                }
            )
        }
    }
}

MenuCard

@Composable
fun MenuCard(
    menu: Menu,
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
) {
    Card(
        shape = MaterialTheme.shapes.small,
        modifier = modifier
            .padding(
                bottom = 6.dp,
                top = 6.dp
            )
            .fillMaxWidth()
            .clickable(onClick = onClick),
        elevation = 8.dp
    ) {
        Column(
            modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(
                text = menu.name,
                fontSize = 30.sp,
                style = MaterialTheme.typography.h3
            )
        }
    }
}

You will get this

3 items 3 items and 6 items 6 items

Rafiul
  • 1,560
  • 7
  • 19
  • I would not recommend this since it adds an extra unnecessary Composable. I used to recommend this method, but recently I found a better approach. See updated answer. – Richard Onslow Roper Nov 08 '21 at 12:02
  • haha, I think the same thing happened in here you said BoxWithConstraint, and others said LocalConfiguration. https://stackoverflow.com/questions/68919900/screen-width-and-height-in-jetpack-compose?rq=1 – Rafiul Nov 08 '21 at 12:21
  • They both have their use cases one always for the whole screen and the other for available space. it takes the remaining space and divides them equally when there are other children on that screen as well. So there is nothing to argue. – Rafiul Nov 08 '21 at 12:25
  • There is a talk https://proandroiddev.com/responsive-layouts-using-boxwithconstraints-in-jetpack-compose-7be64444812f One could argue that we could have used screenWidth from LocalConfiguration Composition Local and still achieve the same result, but then the composable can only be used in places where it fills the whole screen width, not really ideal. Composables should try to consider the size constraints when deciding how to render. When individual composables naturally adapt to the space available, then a screen can become reasonably responsive even if the author of the screen wasn’t.. – Rafiul Nov 08 '21 at 12:29
  • The OP asked specifically about the screen, so technically, the answer that the answerer should give would actually be in reference to the screen. This could be added as an extra info to the context, where it increases its usability, but you see, since the API exists, it is meant to be used. I mean we would not use it if nobody was talking about it, even when the specific use-case for which it is designed is being considered, just like this one. – Richard Onslow Roper Nov 08 '21 at 12:52