What is the best way to get a composable to recalc...
# compose
d
What is the best way to get a composable to recalculate its own height, when a sibling forces a parent to grow? I have an issue where an image of unknown height causes a Row to grow. This in turn messes with a sibling, which has
fillMaxHeight
set. The sibling does not grow to fill the new height of its parent. I have made a gif to illustrate what I mean, the sibling is highlighted blue. If I set the Row to a fixed height, the issue does not happen.
Example code:
Copy code
Row(
    modifier = Modifier.fillMaxHeight()
) {
    // This composable will cause the Row to grow to the size of the image
    CoilImage(data = "<https://picsum.photos/id/15/400/700>")
    
    Spacer(modifier = Modifier.width(8.dp))
    
    // This composable will start out fitting to the height of the 
    // text (since there is nothing yet loaded to compare with). Once 
    // the image is rendered, the box will not continue to fill the space.
    Box(
        modifier = Modifier.fillMaxSize()
            .background(Color.Blue)
    ) {
        Text(
            text = "This is an example"
        )
    }
}
Example gif
z
What happens if you put
background
before
fillMaxSize
?
Also, do you need
fillMaxHeight
on the
Row
? It looks like your rows are in a scrollable list, which measure height as unbounded, which I think means
fillMaxSize
is ignored (if it weren’t ignored, you’d only see one row there and the image size wouldn’t affect it at all).
d
No difference, with your first change. The second one, you are right, the Row doesn't need that. That's an error.
z
Is that a LazyColumn?
d
Yes,
LazyColumnForIndexed
to be specific.
z
The lazy lists do a bunch of fancy stuff with their layouts, I wonder if it’s a bug there, because your layout seems pretty simple and straightforward. What happens if you change it to just be a ScrollableColumn with a for loop inside?
Also i’m not sure this will be helpful, but until the Layout Inspector works with compose, you can use github.com/square/radiography to dump the sizes of your composables to logcat to see what they’re actually being laid out as
d
ScrollableColumn doesn't make a difference. I'll check radiography.
z
This is super hacky, but what if you force invalidate everything on a timer? E.g. something like this:
Copy code
val outerInvalidate = invalidate
Row(…) {
  val innerInvalidate = invalidate
  CoilImage(…)
  …
  LaunchedTask {
    while(isActive) {
      delay(100)
      outerInvalidate()
      innerInvalidate()
    }
  }
}
Also, if you make the image (or the image placeholder) initially take up a larger height (just pass
Modifier.height(300.dp)
or something), do the other siblings get laid out correctly then?
(clearly i’m just guessing here too, i have no idea what’s going on lol)
d
No change unfortunately. It is a bit of a bizarre issue.
Copy code
val outerInvalidate = invalidate

Row {
    val innerInvalidate = invalidate
    // This composable will cause the Row to grow to the size of the image
    CoilImage(
        data = "<https://picsum.photos/id/15/400/700>",
        loading = {
            Box(
                Modifier
                    .width(100.dp)
                    .height(300.dp)
                    .background(Color.Red)
            ) {
                CircularProgressIndicator()
            }
        }
    )
    Spacer(modifier = Modifier.width(8.dp))
    // This composable will start out fitting to the height of the
    // text (since there is nothing yet loaded to compare with). Once
    // the image is rendered, the box will not continue to fill the space.
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Blue)
    ) {
        Text(
            text = "This is an example"
        )
    }

    LaunchedTask {
        while (isActive) {
            delay(100)
            Timber.v("TEST")
            outerInvalidate()
            innerInvalidate()
        }
    }
}
Even with both in action.
z
One more thing to check – replace your
Box
with a custom layout and put breakpoints/log statements in the measureblocks lambda to see if the layout is even being triggered
d
That's something I'll have to research. I haven't tried a custom layout yet. 🔍
z
Something as simple as this should work for debugging:
Copy code
Layout(children = { Box(Modifier) }) { _, _ ->
  println("LAYOUT")
  layout(0, 0) {}
}
d
Looks like it is laying out three times. 🤔
z
what are the constraints? (sorry, should have put this in the original snippet)
Copy code
Layout(children = { Box(Modifier) }) { _, constraints ->
  println("LAYOUT: $constraints")
  layout(0, 0) {}
}
d
This is what it logged out:
Copy code
MyBox: LAYOUT 79895555 Constraints(minWidth = 0, maxWidth = 1328, minHeight = 0, maxHeight = Infinity)
MyBox: LAYOUT 79895555 Constraints(minWidth = 0, maxWidth = 978, minHeight = 0, maxHeight = Infinity)
MyBox: LAYOUT 79895555 Constraints(minWidth = 0, maxWidth = 928, minHeight = 0, maxHeight = Infinity)
z
definitely looks like fillMaxHeight is working, idk what’s going on.
d
Perhaps I should make a repo to reproduce it, if this is not expected behaviour.
z
Might as well, then file a bug. Although maybe in the process it will become apparent what is wrong 🤞
d
🙏 1