---
title: "PullToRefreshBox"
description: "Container that wraps scrollable content to add pull-to-refresh gesture support."
type: "component"
lastmod: "2026-10-07"
---
## API Reference

### PullToRefreshBox

> Source set: Android

```kotlin
@Composable
public fun PullToRefreshBox(
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    state: PullToRefreshState,
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopCenter,
    indicator: @Composable BoxScope.() -> Unit = {
        PullToRefreshDefaults.Indicator(
            modifier = Modifier.align(Alignment.TopCenter),
            isRefreshing = isRefreshing,
            state = state,
        )
    },
    enabled: Boolean = true,
    threshold: Dp = PullToRefreshDefaults.PositionalThreshold,
    content: @Composable BoxScope.() -> Unit,
)
```

#### Parameters

| | |
| --- | --- |
| isRefreshing | whether a refresh is occurring |
| onRefresh | callback invoked when the user gesture crosses the threshold, thereby requesting a refresh. |
| modifier | the [Modifier](/jetpack-compose/androidx.compose.ui/ui/interfaces/Modifier) to be applied to this container |
| state | the state that keeps track of distance pulled |
| contentAlignment | the default alignment inside the Box. |
| indicator | the indicator that will be drawn on top of the content when the user begins a pull or a refresh is occurring |
| enabled | whether nested scroll events should be consumed by this component |
| threshold | how much distance can be scrolled down before `onRefresh` is invoked |
| content | the content of the pull refresh container, typically a scrollable layout such as [androidx.wear.compose.foundation.lazy.TransformingLazyColumn](/jetpack-compose/androidx.wear.compose/compose-foundation/components/TransformingLazyColumn) or a layout using [androidx.compose.foundation.verticalScroll](/jetpack-compose/androidx.compose.foundation/foundation/modifiers/verticalScroll) |

## Code Examples

### PullToRefreshSample

```kotlin
@Sampled
@Composable
@Preview
fun PullToRefreshSample() {
    var itemCount by remember { mutableIntStateOf(10) }
    var isRefreshing by remember { mutableStateOf(false) }
    val coroutineScope = rememberCoroutineScope()
    val listState = rememberTransformingLazyColumnState()
    val transformationSpec = rememberTransformationSpec()
    val state = remember { PullToRefreshState() }

    AppScaffold {
        ScreenScaffold(scrollState = listState, timeText = { TimeText() }) { contentPadding ->
            PullToRefreshBox(
                isRefreshing = isRefreshing,
                onRefresh = {
                    isRefreshing = true
                    coroutineScope.launch {
                        delay(2000)
                        itemCount += 5
                        isRefreshing = false
                    }
                },
                state = state,
                modifier = Modifier.fillMaxSize(),
            ) {
                TransformingLazyColumn(
                    state = listState,
                    contentPadding = contentPadding,
                    modifier = Modifier.fillMaxSize(),
                ) {
                    items(itemCount) { index ->
                        Button(
                            onClick = {},
                            label = { Text("Item ${index + 1}") },
                            transformation = SurfaceTransformation(transformationSpec),
                            modifier =
                                Modifier.transformedHeight(this, transformationSpec)
                                    .minimumVerticalContentPadding(
                                        ButtonDefaults.minimumVerticalListContentPadding
                                    )
                                    .fillMaxWidth(),
                        )
                    }
                }
            }
        }
    }
}
```

### PullToRefreshCustomIndicatorSample

```kotlin
@Sampled
@Composable
@Preview
fun PullToRefreshCustomIndicatorSample() {
    var isRefreshing by remember { mutableStateOf(false) }
    val coroutineScope = rememberCoroutineScope()
    val state = remember { PullToRefreshState() }
    val listState = rememberTransformingLazyColumnState()
    val transformationSpec = rememberTransformationSpec()

    AppScaffold {
        ScreenScaffold(scrollState = listState, timeText = { TimeText() }) { contentPadding ->
            PullToRefreshBox(
                isRefreshing = isRefreshing,
                onRefresh = {
                    isRefreshing = true
                    coroutineScope.launch {
                        delay(2000)
                        isRefreshing = false
                    }
                },
                state = state,
                indicator = {
                    PullToRefreshDefaults.IndicatorBox(
                        state = state,
                        isRefreshing = isRefreshing,
                        shape = MaterialTheme.shapes.medium,
                        containerColor = MaterialTheme.colorScheme.primaryContainer,
                        modifier = Modifier.align(Alignment.TopCenter),
                    ) {
                        if (isRefreshing) {
                            CircularProgressIndicator(
                                strokeWidth = 2.5.dp,
                                colors =
                                    ProgressIndicatorDefaults.colors(
                                        indicatorColor =
                                            MaterialTheme.colorScheme.onPrimaryContainer
                                    ),
                                modifier = Modifier.size(24.dp),
                            )
                        } else {
                            val rotation = state.distanceFraction * 180f
                            val scale = state.distanceFraction.coerceIn(0f, 1f)
                            Icon(
                                imageVector = Icons.Default.Refresh,
                                contentDescription = "Pull to refresh",
                                tint = MaterialTheme.colorScheme.onPrimaryContainer,
                                modifier =
                                    Modifier.size(24.dp).graphicsLayer {
                                        rotationZ = rotation
                                        scaleX = scale
                                        scaleY = scale
                                    },
                            )
                        }
                    }
                },
                modifier = Modifier.fillMaxSize(),
            ) {
                TransformingLazyColumn(
                    state = listState,
                    contentPadding = contentPadding,
                    modifier = Modifier.fillMaxSize(),
                ) {
                    items(10) { index ->
                        Button(
                            onClick = {},
                            label = { Text("Custom item ${index + 1}") },
                            transformation = SurfaceTransformation(transformationSpec),
                            modifier =
                                Modifier.transformedHeight(this, transformationSpec)
                                    .minimumVerticalContentPadding(
                                        ButtonDefaults.minimumVerticalListContentPadding
                                    )
                                    .fillMaxWidth(),
                        )
                    }
                }
            }
        }
    }
}
```
