---
title: "PullToRefreshBox"
description: "`PullToRefreshBox` is a container that expects a scrollable layout as content and adds gesture
support for manually refreshing when the user swipes downward at the beginning of the content. By
default, it uses `PullToRefreshDefaults.Indicator` as the refresh indicator, but you may also
choose to set your own indicator or use `PullToRefreshDefaults.LoadingIndicator`."
type: "component"
---

<div class='type'>Composable Component</div>



`PullToRefreshBox` is a container that expects a scrollable layout as content and adds gesture
support for manually refreshing when the user swipes downward at the beginning of the content. By
default, it uses `PullToRefreshDefaults.Indicator` as the refresh indicator, but you may also
choose to set your own indicator or use `PullToRefreshDefaults.LoadingIndicator`.

<a id='references'></a>

<div class='sourceset sourceset-common'>Common</div>


```kotlin
@Composable
fun PullToRefreshBox(
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier,
    state: PullToRefreshState = rememberPullToRefreshState(),
    contentAlignment: Alignment = Alignment.TopStart,
    indicator: @Composable BoxScope.() -> Unit = {
        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` 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 `LazyColumn` or a layout using `androidx.compose.foundation.verticalScroll` |




<div class='sourceset sourceset-common'>Common</div>


> **Deprecated** Use the PullToRefreshBox that takes enabled and threshold parameters

```kotlin
@Composable
fun PullToRefreshBox(
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier,
    state: PullToRefreshState = rememberPullToRefreshState(),
    contentAlignment: Alignment = Alignment.TopStart,
    indicator: @Composable BoxScope.() -> Unit = {
        Indicator(
            modifier = Modifier.align(Alignment.TopCenter),
            isRefreshing = isRefreshing,
            state = state,
        )
    },
    content: @Composable BoxScope.() -> Unit,
)
```




## Code Examples
### PullToRefreshCustomIndicatorWithDefaultTransform
```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun PullToRefreshCustomIndicatorWithDefaultTransform() {
    var itemCount by remember { mutableIntStateOf(15) }
    var isRefreshing by remember { mutableStateOf(false) }
    val state = rememberPullToRefreshState()
    val coroutineScope = rememberCoroutineScope()
    val onRefresh: () -> Unit = {
        isRefreshing = true
        coroutineScope.launch {
            delay(1500)
            itemCount += 5
            isRefreshing = false
        }
    }
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Title") },
                // Provide an accessible alternative to trigger refresh.
                actions = {
                    IconButton(onClick = onRefresh) {
                        Icon(Icons.Filled.Refresh, "Trigger Refresh")
                    }
                },
            )
        }
    ) {
        PullToRefreshBox(
            modifier = Modifier.padding(it),
            state = state,
            isRefreshing = isRefreshing,
            onRefresh = onRefresh,
            indicator = {
                PullToRefreshDefaults.IndicatorBox(
                    state = state,
                    isRefreshing = isRefreshing,
                    modifier = Modifier.align(Alignment.TopCenter),
                    elevation = 0.dp,
                ) {
                    if (isRefreshing) {
                        CircularProgressIndicator()
                    } else {
                        CircularProgressIndicator(
                            progress = { state.distanceFraction },
                            trackColor = ProgressIndicatorDefaults.circularIndeterminateTrackColor,
                        )
                    }
                }
            },
        ) {
            LazyColumn(Modifier.fillMaxSize()) {
                items(itemCount) { ListItem({ Text(text = "Item ${itemCount - it}") }) }
            }
        }
    }
}
```
### PullToRefreshSample
```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun PullToRefreshSample() {
    var itemCount by remember { mutableIntStateOf(15) }
    var isRefreshing by remember { mutableStateOf(false) }
    val state = rememberPullToRefreshState()
    val coroutineScope = rememberCoroutineScope()
    val onRefresh: () -> Unit = {
        isRefreshing = true
        coroutineScope.launch {
            delay(5000)
            itemCount += 5
            isRefreshing = false
        }
    }
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Title") },
                // Provide an accessible alternative to trigger refresh.
                actions = {
                    IconButton(onClick = onRefresh) {
                        Icon(Icons.Filled.Refresh, "Trigger Refresh")
                    }
                },
            )
        }
    ) {
        PullToRefreshBox(
            modifier = Modifier.padding(it),
            state = state,
            isRefreshing = isRefreshing,
            onRefresh = onRefresh,
        ) {
            LazyColumn(Modifier.fillMaxSize()) {
                items(itemCount) { ListItem({ Text(text = "Item ${itemCount - it}") }) }
            }
        }
    }
}
```
### PullToRefreshSampleCustomState
```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun PullToRefreshSampleCustomState() {
    var itemCount by remember { mutableIntStateOf(15) }
    var isRefreshing by remember { mutableStateOf(false) }
    val coroutineScope = rememberCoroutineScope()
    val onRefresh: () -> Unit = {
        isRefreshing = true
        coroutineScope.launch {
            // fetch something
            delay(5000)
            itemCount += 5
            isRefreshing = false
        }
    }
    val state = remember {
        object : PullToRefreshState {
            private val anim = Animatable(0f, Float.VectorConverter)
            override val distanceFraction
                get() = anim.value
            override val isAnimating: Boolean
                get() = anim.isRunning
            override suspend fun animateToThreshold() {
                anim.animateTo(1f, spring(dampingRatio = Spring.DampingRatioHighBouncy))
            }
            override suspend fun animateToHidden() {
                anim.animateTo(0f)
            }
            override suspend fun snapTo(targetValue: Float) {
                anim.snapTo(targetValue)
            }
        }
    }
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("TopAppBar") },
                // Provide an accessible alternative to trigger refresh.
                actions = {
                    IconButton(onClick = onRefresh) {
                        Icon(Icons.Filled.Refresh, "Trigger Refresh")
                    }
                },
            )
        }
    ) {
        PullToRefreshBox(
            modifier = Modifier.padding(it),
            isRefreshing = isRefreshing,
            onRefresh = onRefresh,
            state = state,
        ) {
            LazyColumn(Modifier.fillMaxSize()) {
                if (!isRefreshing) {
                    items(itemCount) { ListItem({ Text(text = "Item ${itemCount - it}") }) }
                }
            }
        }
    }
}
```
### PullToRefreshScalingSample
```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun PullToRefreshScalingSample() {
    var itemCount by remember { mutableIntStateOf(15) }
    var isRefreshing by remember { mutableStateOf(false) }
    val state = rememberPullToRefreshState()
    val coroutineScope = rememberCoroutineScope()
    val onRefresh: () -> Unit = {
        isRefreshing = true
        coroutineScope.launch {
            // fetch something
            delay(5000)
            itemCount += 5
            isRefreshing = false
        }
    }
    val scaleFraction = {
        if (isRefreshing) 1f
        else LinearOutSlowInEasing.transform(state.distanceFraction).coerceIn(0f, 1f)
    }
    Scaffold(
        modifier =
            Modifier.pullToRefresh(
                state = state,
                isRefreshing = isRefreshing,
                onRefresh = onRefresh,
            ),
        topBar = {
            TopAppBar(
                title = { Text("TopAppBar") },
                // Provide an accessible alternative to trigger refresh.
                actions = {
                    IconButton(onClick = onRefresh) {
                        Icon(Icons.Filled.Refresh, "Trigger Refresh")
                    }
                },
            )
        },
    ) {
        Box(Modifier.padding(it)) {
            LazyColumn(Modifier.fillMaxSize()) {
                if (!isRefreshing) {
                    items(itemCount) { ListItem({ Text(text = "Item ${itemCount - it}") }) }
                }
            }
            Box(
                Modifier.align(Alignment.TopCenter).graphicsLayer {
                    scaleX = scaleFraction()
                    scaleY = scaleFraction()
                }
            ) {
                PullToRefreshDefaults.Indicator(state = state, isRefreshing = isRefreshing)
            }
        }
    }
}
```
### PullToRefreshViewModelSample
```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun PullToRefreshViewModelSample() {
    val viewModel = remember {
        object : ViewModel() {
            private val refreshRequests = Channel<Unit>(1)
            var isRefreshing by mutableStateOf(false)
                private set
            var itemCount by mutableIntStateOf(15)
                private set
            init {
                viewModelScope.launch {
                    for (r in refreshRequests) {
                        isRefreshing = true
                        try {
                            itemCount += 5
                            delay(5000) // simulate doing real work
                        } finally {
                            isRefreshing = false
                        }
                    }
                }
            }
            fun refresh() {
                refreshRequests.trySend(Unit)
            }
        }
    }
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Title") },
                // Provide an accessible alternative to trigger refresh.
                actions = {
                    IconButton(
                        enabled = !viewModel.isRefreshing,
                        onClick = { viewModel.refresh() },
                    ) {
                        Icon(Icons.Filled.Refresh, "Trigger Refresh")
                    }
                },
            )
        }
    ) {
        PullToRefreshBox(
            modifier = Modifier.padding(it),
            isRefreshing = viewModel.isRefreshing,
            onRefresh = { viewModel.refresh() },
        ) {
            LazyColumn(Modifier.fillMaxSize()) {
                if (!viewModel.isRefreshing) {
                    items(viewModel.itemCount) {
                        ListItem({ Text(text = "Item ${viewModel.itemCount - it}") })
                    }
                }
            }
        }
    }
}
```
### PullToRefreshWithLoadingIndicatorSample
```kotlin
@Composable
@Preview
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
fun PullToRefreshWithLoadingIndicatorSample() {
    var itemCount by remember { mutableIntStateOf(15) }
    var isRefreshing by remember { mutableStateOf(false) }
    val state = rememberPullToRefreshState()
    val coroutineScope = rememberCoroutineScope()
    val onRefresh: () -> Unit = {
        isRefreshing = true
        coroutineScope.launch {
            delay(5000)
            itemCount += 5
            isRefreshing = false
        }
    }
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Title") },
                // Provide an accessible alternative to trigger refresh.
                actions = {
                    IconButton(onClick = onRefresh) {
                        Icon(Icons.Filled.Refresh, "Trigger Refresh")
                    }
                },
            )
        }
    ) {
        PullToRefreshBox(
            modifier = Modifier.padding(it),
            state = state,
            isRefreshing = isRefreshing,
            onRefresh = onRefresh,
            indicator = {
                PullToRefreshDefaults.LoadingIndicator(
                    state = state,
                    isRefreshing = isRefreshing,
                    modifier = Modifier.align(Alignment.TopCenter),
                )
            },
        ) {
            LazyColumn(Modifier.fillMaxSize()) {
                items(itemCount) { ListItem({ Text(text = "Item ${itemCount - it}") }) }
            }
        }
    }
}
```

