Build apps faster with our new App builder! Check it out →

pullRefreshIndicatorTransform

Common

Modifier in Material Compose

A modifier for translating the position and scaling the size of a pull-to-refresh indicator based on the given [PullRefreshState].

Last updated:

Installation

dependencies {
   implementation("androidx.compose.material:material:1.8.0-alpha04")
}

Overloads

@ExperimentalMaterialApi
fun Modifier.pullRefreshIndicatorTransform(
    state: PullRefreshState,
    scale: Boolean = false,
) =
    // Essentially we only want to clip the at the top, so the indicator will not appear when
    // the position is 0. It is preferable to clip the indicator as opposed to the layout that
    // contains the indicator, as this would also end up clipping shadows drawn by items in a
    // list for example - so we leave the clipping to the scrolling container. We use MAX_VALUE
    // for the other dimensions to allow for more room for elevation / arbitrary indicators - we
    // only ever really want to clip at the top edge.

Parameters

namedescription
stateThe [PullRefreshState] which determines the position of the indicator.
scaleA boolean controlling whether the indicator's size scales with pull progress or not.

Code Example

PullRefreshIndicatorTransformSample

/**
 * An example to show how [pullRefreshIndicatorTransform] can be given custom contents to create a
 * custom indicator.
 */
@Composable
@OptIn(ExperimentalMaterialApi::class)
fun PullRefreshIndicatorTransformSample() {
    val refreshScope = rememberCoroutineScope()
    var refreshing by remember { mutableStateOf(false) }
    var itemCount by remember { mutableStateOf(15) }

    fun refresh() =
        refreshScope.launch {
            refreshing = true
            delay(1500)
            itemCount += 5
            refreshing = false
        }

    val state = rememberPullRefreshState(refreshing, ::refresh)
    val rotation = animateFloatAsState(state.progress * 120)

    Box(Modifier.fillMaxSize().pullRefresh(state)) {
        LazyColumn {
            if (!refreshing) {
                items(itemCount) { ListItem { Text(text = "Item ${itemCount - it}") } }
            }
        }

        Surface(
            modifier =
                Modifier.size(40.dp)
                    .align(Alignment.TopCenter)
                    .pullRefreshIndicatorTransform(state)
                    .rotate(rotation.value),
            shape = RoundedCornerShape(10.dp),
            color = Color.DarkGray,
            elevation = if (state.progress > 0 || refreshing) 20.dp else 0.dp,
        ) {
            Box {
                if (refreshing) {
                    CircularProgressIndicator(
                        modifier = Modifier.align(Alignment.Center).size(25.dp),
                        color = Color.White,
                        strokeWidth = 3.dp
                    )
                }
            }
        }
    }
}
by @alexstyl