---
title: "PullToRefreshState"
description: "The state of a [PullToRefreshBox] which tracks the distance that the container and indicator have been pulled."
type: "class"
lastmod: "2026-10-07"
---
## API Reference

> Source set: Android

```kotlin
public class PullToRefreshState internal constructor(private val anim: Animatable<Float, AnimationVector1D>)
```

The state of a [PullToRefreshBox](/jetpack-compose/androidx.wear.compose/compose-material3/components/PullToRefreshBox/api) which tracks the distance that the container and indicator have
been pulled.

Each instance of [PullToRefreshBox](/jetpack-compose/androidx.wear.compose/compose-material3/components/PullToRefreshBox/api) should have its own [PullToRefreshState](/jetpack-compose/androidx.wear.compose/compose-material3/classes/PullToRefreshState/api).

[PullToRefreshState](/jetpack-compose/androidx.wear.compose/compose-material3/classes/PullToRefreshState/api) can be used with custom progress indicators like so:

## Properties

### distanceFraction

> Source set: Android

```kotlin
public val distanceFraction: Float
```

Distance percentage towards the refresh threshold. 0.0 indicates no distance, 1.0 indicates
being at the threshold offset, > 1.0 indicates overshoot beyond the provided threshold.

### isAnimating

> Source set: Android

```kotlin
public val isAnimating: Boolean
```

Whether the state is currently animating the indicator to the threshold offset, or back to
the hidden offset.

## Functions

### animateToThreshold

> Source set: Android

```kotlin
public suspend fun animateToThreshold(animationSpec: AnimationSpec<Float> = spring())
```

Animate the distance towards the anchor or threshold position, where the indicator will be
shown when refreshing.

#### Parameters

| | |
| --- | --- |
| animationSpec | The [AnimationSpec](/jetpack-compose/androidx.compose.animation/animation-core/interfaces/AnimationSpec) used for animating the distance. |

### animateToHidden

> Source set: Android

```kotlin
public suspend fun animateToHidden(animationSpec: AnimationSpec<Float> = spring())
```

Animate the distance towards the position where the indicator will be hidden when idle.

#### Parameters

| | |
| --- | --- |
| animationSpec | The [AnimationSpec](/jetpack-compose/androidx.compose.animation/animation-core/interfaces/AnimationSpec) used for animating the distance. |

### snapTo

> Source set: Android

```kotlin
public suspend fun snapTo(@FloatRange(from = 0.0) targetValue: Float)
```

Snap the indicator to the desired threshold fraction.

#### Parameters

| | |
| --- | --- |
| targetValue | The target fraction to snap to, where 0.0 represents the hidden state and 1.0 represents the threshold position. |

## Code Examples

### 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(),
                        )
                    }
                }
            }
        }
    }
}
```
