Thread
#compose
    b

    Bradleycorn

    1 year ago
    Does anyone know if there are plans to make
    SubComposeLayout
    and
    Intrinsics
    work together? For example something like the following:
    Row(modifier = Modifier.fillMaxWidth.height(IntrinsicSize.Min)) {
        BoxWithConstraints(...) {
            ...
        }
    }
    this currently crashes with an IllegalStateException with a message of
    Intrinsic measurements are not currently supported by SubcomposeLayout
    Any way to “fix” this?
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    Have you considered just making a custom layout instead?
    b

    Bradleycorn

    1 year ago
    hmm, I had not. I had not. In my particular use case, I could eliminate the
    Row
    and use a
    ConstraintLayout
    and then I wouldn’t need to use Intrinsics for the height. I posted the question for 2 reasons:1. To find out if if this is something that won’t (or can’t) ever be “fixed”, so I need to learn other tools to deal with situations like this, and 2. What those other tools might be. But yeah, I custom layout might be one of those tools.
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    Can you post more code?
    b

    Bradleycorn

    1 year ago
    @Zach Klippenstein (he/him) [MOD] sorry, got sidetracked yesterday. Sure I can post more code. The TL😄R is, I’m using
    BoxWithConstraints
    to set the fontSize on a
    Text
    dynamically based on the Box’s width (it’s a reusable component that can by any size). If I could figure out a (decent) way to do that without
    SubComposeLayout
    , it would solve my problems. The other option is to figure out the best way to NOT use Intrinsics for the container’s height. See below for the details on the container.
    So, first off, I have a
    Row
    (it’s an item in a Lazy Column). It has several things, but there are 2 that matter. There is a
    Column
    in the Row that contains three
    Text
    composables, and essentially the Row’s height should wrap the Column. Next to the column is a
    SaddleCloth
    composable, which is a wrapper for a
    BoxWithConstraints
    . The
    SaddleCloth
    composable is a reusable component that can be any size, and uses the
    BoxWithConstraints
    to set the fontSize on the
    Text
    it contains dynamically, based on it’s width. it also has a pretty complex background that is drawn from a
    Path
    (to create things like stripes and triangles).
    @Composable
    fun RunnerRow(runner: Runner, race: Race) {
    
        Row(modifier = Modifier
            .fillMaxWidth()
            .height(IntrinsicSize.Min)
            .padding(end = 8.dp)) {
    
            SaddleCloth(
                programNumber = runner.programNumber,
                trackType = race.raceId.trackId.trackType,
                bettingInterest = runner.bettingInterest,
                modifier = Modifier.width(24.dp).fillMaxHeight()
            )
    
            Column(
                modifier = Modifier
                    .weight(1F)
                    .padding(horizontal = 8.dp, vertical = 6.dp)
            ) {
                Text(text = runner.name, fontWeight = FontWeight.Bold)
                Text(text = runner.jockey, style = MaterialTheme.typography.caption)
                Text(text = runner.trainer, style = MaterialTheme.typography.caption)
            }
        }
    }
    And as I mentioned, the
    SaddleCloth
    wraps a
    BoxWithConstraints
    and sets the fontSize based on it’s width (which is not always fixed. The composable is used in several places with varying sizes).. In the above
    Row
    , I want the
    SaddleCloth
    to fill the height of the
    Row
    (which should be determined by wrapping the Column of 3 Text composables). Using Intrinsics works great for that, except that the SaddleCloth is a BoxWithConstraints, which uses SubComposeLayout.
    @Composable
    fun SaddleCloth(programNumber: String, trackType: TrackType, bettingInterest: Int, modifier: Modifier = Modifier) {
        val saddleCloth = remember(trackType, bettingInterest) { saddleCloths.forRunner(trackType, bettingInterest) }
    
        val boxModifier = when {
            modifier.any { it is LayoutModifier } -> modifier
            else -> modifier.fillMaxSize()
        }
    
        BoxWithConstraints(
            modifier = boxModifier
                .drawWithCache {
                    // Draw a background using vector [Path]s based on the saddlecloth type.
                    onDrawBehind {
                        when (saddleCloth.type) {
                            is SaddleClothType.Solid -> drawRect(color = saddleCloth.type.primaryColor)
                            is SaddleClothType.Triangles -> drawTriangles(
                                size = size,
                                topColor = saddleCloth.type.primaryColor,
                                bottomColor = saddleCloth.type.bottomColor)
                            is SaddleClothType.VerticalStripes -> drawVerticalStripes(
                                size = size,
                                baseColor = saddleCloth.type.primaryColor,
                                stripeColor = saddleCloth.type.stripeColor)
                            is SaddleClothType.HorizontalStripes -> drawHorizontalStripes(
                                size = size,
                                baseColor = saddleCloth.type.primaryColor,
                                stripeColor = saddleCloth.type.stripeColor)
                        }
                    }
                },
            contentAlignment = Alignment.Center
        ) {
            val fontSize = maxWidth * 0.4f
    
            Text(
                text = programNumber,
                textAlign = TextAlign.Center,
                fontSize = LocalDensity.current.run { fontSize.toSp() },
                fontWeight = FontWeight.Bold,
                color = saddleCloth.contentColor)
        }
    }
    The
    SaddleCloth
    composable renders like this:
    And the overall look I’m going for is:
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    Ok, I think I understand. Since you don’t need the intrinsic height of the saddlecloth component to calculate height, I think you could probably wrap it with something that would just provide a meaningless intrinsic height of zero so the row effectively ignores it.
    Alternatively, a relatively simple custom layout could just measure the column first and then use the measured size to provide tight constraints to the saddlecloth (measurement doesn’t need to happen in the same order as composition).
    b

    Bradleycorn

    1 year ago
    so for the first suggestion… I think you are saying that basically you could “trick” the row into thinking that the SaddleCloth is 0 height, so then the Row can use the
    wrapContentHeight
    modifier, and then the SaddleCloth is fine to use
    SubComposeLayout
    (since there are no more intrinsics) and can
    fillMaxHeight
    and it’ll get measured and composed as shown above … do I have that right?
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    Not quite - by explicitly reporting a zero intrinsic height, the row won’t use the SC’s height when calculating its own height. Then you can still use fillMaxHeight on the SC itself to match the intrinsic height of the column of texts.
    In terms of passes, intrinsic height is basically a super cheap, pre-calculated constraint that a layout can query before measuring. That’s useful because measurement requires passing constraints in, so intrinsic sizes allows the row to figure out what the constraints should be for all its children by asking each of them how big they want to be first. So even if you report a zero intrinsic height you should still be able to measure yourself at whatever height is appropriate- including the max height your layout has passed you in the constraints. Anyway, I haven’t tried it, but I think that should work based on my understanding of intrinsics. Even if it does though, I think the custom layout might actually be less hacky and easier to read because you can make the layout intent crystal clear without leaving your code readers to figure out how the intrinsics all work together.
    b

    Bradleycorn

    1 year ago
    Yeah I like the custom layout idea too.
    I’m still a little confused on the first part …
    by explicitly reporting a zero intrinsic height
    how would I do that?
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    you might actually need to implement a custom (child) layout or layout modifier to do it. so that would really obscure your intent, and make an even greater argument for just using a custom layout for the row
    b

    Bradleycorn

    1 year ago
    ah ok. I kept looking for
    IntrinsicSize.ZERO
    or something … thought I was going crazy for a minute there 🙂
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    yea, the whole intrinsics approach is super hacky and i feel worse about even suggesting it the more i think about it 😅 I’m not super familiar with all the intrinsics APIs, but i’m guessing if there’s no convenient way to do this it’s because it’s just better to use a custom layout anyway
    b

    Bradleycorn

    1 year ago
    Yeah proabably so. And it would give me a good chance to actually internalize the concepts behind custom layouts by building something useful of my own. To this point I’ve done the codelabs and what not where they teach you the custom layout basics, but I haven’t really explored it yet to get a really good grasp on it.