Desliza para actualizar

El componente de arrastrar para actualizar permite a los usuarios arrastrar hacia abajo al principio del contenido de una app para actualizar los datos.

Superficie de la API

Usa el elemento PullToRefreshBox componible para implementar la función de actualizar con un gesto de desplazamiento, que actúa como un contenedor para tu contenido desplazable. Los siguientes parámetros clave controlan el comportamiento y la apariencia de la actualización:

  • isRefreshing: Es un valor booleano que indica si la acción de actualización está en curso.
  • onRefresh: Es una función lambda que se ejecuta cuando el usuario inicia una actualización.
  • indicator: Personaliza el indicador que dibuja el sistema en la acción de actualizar con desplazamiento.

Ejemplo básico

En este fragmento, se muestra el uso básico de PullToRefreshBox:

@Composable
fun PullToRefreshBasicSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

Puntos clave sobre el código

  • PullToRefreshBox contiene un LazyColumn, que muestra una lista de cadenas.
  • PullToRefreshBox requiere los parámetros isRefreshing y onRefresh.
  • El contenido dentro del bloque PullToRefreshBox representa el contenido desplazable.

Resultado

En este video, se muestra la implementación básica de la función de arrastrar para actualizar del código anterior:

Figura 1. Implementación básica de la función de arrastrar para actualizar en una lista de elementos.

Ejemplo avanzado: Personaliza el color del indicador

@Composable
fun PullToRefreshCustomStyleSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            Indicator(
                modifier = Modifier.align(Alignment.TopCenter),
                isRefreshing = isRefreshing,
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                color = MaterialTheme.colorScheme.onPrimaryContainer,
                state = state
            )
        },
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

Puntos clave sobre el código

  • El color del indicador se personaliza a través de las propiedades containerColor y color en el parámetro indicator.
  • rememberPullToRefreshState() administra el estado de la acción de actualización. Usas este estado junto con el parámetro indicator.

Resultado

En este video, se muestra una implementación de la función de arrastrar para actualizar con un indicador de color:

Figura 2. Implementación de la función de arrastrar para actualizar con un diseño personalizado.

Ejemplo avanzado: Crea un indicador totalmente personalizado

Puedes crear indicadores personalizados complejos aprovechando los elementos componibles y las animaciones existentes.Este fragmento muestra cómo crear un indicador completamente personalizado en tu implementación de actualizar para cargar:

@Composable
fun PullToRefreshCustomIndicatorSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            MyCustomIndicator(
                state = state,
                isRefreshing = isRefreshing,
                modifier = Modifier.align(Alignment.TopCenter)
            )
        }
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

// ...
@Composable
fun MyCustomIndicator(
    state: PullToRefreshState,
    isRefreshing: Boolean,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier.pullToRefreshIndicator(
            state = state,
            isRefreshing = isRefreshing,
            containerColor = PullToRefreshDefaults.containerColor,
            threshold = PositionalThreshold
        ),
        contentAlignment = Alignment.Center
    ) {
        Crossfade(
            targetState = isRefreshing,
            animationSpec = tween(durationMillis = CROSSFADE_DURATION_MILLIS),
            modifier = Modifier.align(Alignment.Center)
        ) { refreshing ->
            if (refreshing) {
                CircularProgressIndicator(Modifier.size(SPINNER_SIZE))
            } else {
                val distanceFraction = { state.distanceFraction.coerceIn(0f, 1f) }
                Icon(
                    imageVector = Icons.Filled.CloudDownload,
                    contentDescription = "Refresh",
                    modifier = Modifier
                        .size(18.dp)
                        .graphicsLayer {
                            val progress = distanceFraction()
                            this.alpha = progress
                            this.scaleX = progress
                            this.scaleY = progress
                        }
                )
            }
        }
    }
}

Puntos clave sobre el código

  • En el fragmento anterior, se usó Indicator proporcionado por la biblioteca. Este fragmento crea un elemento componible de indicador personalizado llamado MyCustomIndicator. En este elemento componible, el modificador pullToRefreshIndicator controla el posicionamiento y activa una actualización.
  • Al igual que en el fragmento anterior, el ejemplo extrae la instancia de PullToRefreshState, por lo que puedes pasar la misma instancia a PullToRefreshBox y pullToRefreshModifier.
  • En el ejemplo, se usan el color del contenedor y el umbral de posición de la clase PullToRefreshDefaults. De esta manera, puedes reutilizar el comportamiento y el diseño predeterminados de la biblioteca de Material, y personalizar solo los elementos que te interesen.
  • MyCustomIndicator usa Crossfade para realizar la transición entre un ícono de nube y un CircularProgressIndicator. El ícono de nube se agranda a medida que el usuario tira hacia abajo y realiza una transición a CircularProgressIndicator cuando comienza la acción de actualización.
    • targetState usa isRefreshing para determinar qué estado mostrar (el ícono de nube o el indicador de progreso circular).
    • animationSpec define una animación tween para la transición, con una duración especificada de CROSSFADE_DURATION_MILLIS.
    • state.distanceFraction representa qué tanto tiró hacia abajo el usuario, desde 0f (sin tirar) hasta 1f (tirar por completo).
    • El modificador graphicsLayer modifica la escala y la transparencia.

Resultado

En este video, se muestra el indicador personalizado del código anterior:

Figura 3. Implementación de la función de arrastrar para actualizar con un indicador personalizado.

Recursos adicionales