Home > Back-end >  Using Modifier.fillMaxHeight() in Box doesn't display the element
Using Modifier.fillMaxHeight() in Box doesn't display the element

Time:10-21

I am trying to display a shadow at the start and end of a list to indicate that the list still has contents to the left/right.

I want the shadow to be as tall as the whole row so therefore I use Modifier.fillMaxHeight(). Unfortunately this doesn't work unless I specifiy the size of the component at call time.

When I call the function as:

LazyRowWithShadows(modifier = Modifier
    .fillMaxWidth(),
    contentPadding = PaddingValues(horizontal = 12.dp),
    shadowColor = colorResource(R.color.blue_white),
    shadowAlphaStart = 0f, shadowAlphaEnd = 0.8f,
    shadowWidth = 48.dp
) {
    // Content
}

The shadows are not displayed, but the rest of the content is. I have to give the modifier a height to make it work eg:

LazyRowWithShadows(modifier = Modifier
    .fillMaxWidth()
    .height(X.dp), // This is what makes it display, but requires knowing the height of the row
    contentPadding = PaddingValues(horizontal = 12.dp),
    shadowColor = colorResource(R.color.blue_white),
    shadowAlphaStart = 0f, shadowAlphaEnd = 0.8f,
    shadowWidth = 48.dp
) {
    // Content
}

I would prefer if the LazyRowWithShadows handled displaying the shadows even if no size is specified when calling it. Is there an easy way to do this?

This is the code for the LazyRowWithShadows:

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**  A LazyRow that shows start & end shadows when content is not fully scrolled. */
@ExperimentalAnimationApi
@Composable
fun LazyRowWithShadows(
        modifier: Modifier = Modifier,
        contentPadding: PaddingValues = PaddingValues(0.dp),
        shadowColor: Color = Color.Black,
        shadowAlphaStart: Float = 0f,
        shadowAlphaEnd: Float = 0.3f,
        shadowWidth: Dp = 32.dp,
        content: LazyListScope.() -> Unit
) {
    val listState = rememberLazyListState()

    val showStartShadow by remember {
        derivedStateOf {
            listState.firstVisibleItemScrollOffset > 0 || listState.firstVisibleItemIndex > 0
        }
    }

    val showEndShadow by remember {
        derivedStateOf {
            val lastItemIfVisible = listState.layoutInfo.visibleItemsInfo.maxByOrNull { it.index }?.takeIf { it.index == listState.layoutInfo.totalItemsCount - 1 }
            if (lastItemIfVisible != null) {
                val lastItemEndX = lastItemIfVisible.offset   lastItemIfVisible.size
                lastItemEndX > listState.layoutInfo.viewportEndOffset
            } else {
                true
            }
        }
    }

    Box(modifier = modifier) {
        LazyRow(
                state = listState,
                contentPadding = contentPadding,
                content = content)

        // Start scroll shadow
        AnimatedVisibility(visible = showStartShadow, modifier = Modifier.align(Alignment.CenterStart)) {
            Box(modifier = Modifier
                    .fillMaxHeight()
                    .width(shadowWidth)
                    .background(brush = Brush.horizontalGradient(colors = listOf(shadowColor.copy(alpha = shadowAlphaEnd), shadowColor.copy(alpha = shadowAlphaStart))))
            )
        }
        // End scroll shadow
        AnimatedVisibility(visible = showEndShadow, modifier = Modifier.align(Alignment.CenterEnd)) {
            Box(modifier = Modifier
                    .fillMaxHeight()
                    .width(shadowWidth)
                    .background(brush = Brush.horizontalGradient(colors = listOf(shadowColor.copy(alpha = shadowAlphaStart), shadowColor.copy(alpha = shadowAlphaEnd))))
            )
        }
    }
}

Edit: I have tried the solution proposed in fillMaxSize modifier not working when combined with VerticalScroll in Jetpack Compose, but it didn't solve my issue. The changes I made to try to solve it using that answer as inspiration are:

BoxWithConstraints(modifier = modifier) { // Changed to BoxWithConstraints
    LazyRow(
        state = listState,
        contentPadding = contentPadding,
        content = content)

    // Start scroll shadow
    AnimatedVisibility(visible = showStartShadow, modifier = Modifier.align(Alignment.CenterStart)) {
        Box(modifier = Modifier
            .height(this@BoxWithConstraints.maxHeight) // Changed from fillMaxHeight
            .width(shadowWidth)
            .background(brush = Brush.horizontalGradient(colors = listOf(shadowColor.copy(alpha = shadowAlphaEnd), shadowColor.copy(alpha = shadowAlphaStart))))
        )
    }
    // End scroll shadow
    AnimatedVisibility(visible = showEndShadow, modifier = Modifier.align(Alignment.CenterEnd)) {
        Box(modifier = Modifier
            .height(this@BoxWithConstraints.maxHeight) // Changed from fillMaxHeight
            .width(shadowWidth)
            .background(brush = Brush.horizontalGradient(colors = listOf(shadowColor.copy(alpha = shadowAlphaStart), shadowColor.copy(alpha = shadowAlphaEnd))))
        )
    }
}

I also tried adding a height to the AnimatedVisibility elements, but that didn't change anything.

CodePudding user response:

After trying out a couple of ways, using Modifier.onSizeChanged to get the height of LazyRow finally made it work.

Convert this height to dp and pass it to Modifier.height for shadow's Box(s).

Box(modifier = modifier) {
    val density = LocalDensity.current
    var height by remember { mutableStateOf(0) }
    val heightDp = remember(height) { with(density){ height.toDp() } }
    LazyRow(
        modifier = Modifier.onSizeChanged {
            height = it.height
        },
        state = listState,
        contentPadding = contentPadding,
        content = content)

    // Start scroll shadow
    AnimatedVisibility(visible = showStartShadow, modifier = Modifier.align(Alignment.CenterStart)) {
        Box(modifier = Modifier
            .height(heightDp)
            .width(shadowWidth)
            .background(brush = Brush.horizontalGradient(colors = listOf(shadowColor.copy(alpha = shadowAlphaEnd), shadowColor.copy(alpha = shadowAlphaStart))))
        )
    }
    // End scroll shadow
    AnimatedVisibility(visible = showEndShadow, modifier = Modifier.align(Alignment.CenterEnd)) {
        Box(modifier = Modifier
            .height(heightDp)
            .width(shadowWidth)
            .background(brush = Brush.horizontalGradient(colors = listOf(shadowColor.copy(alpha = shadowAlphaStart), shadowColor.copy(alpha = shadowAlphaEnd))))
        )
    }
}
  • Related