rslabdocs

Pull Refresh Island

A gooey pull-to-refresh where a water drop is pulled out of the Dynamic Island.

ReanimatedSkiaGesture HandlerSource

Prerequisites

This animation is built on the following libraries. expo install pins the versions that match your SDK, and the CLI tells you exactly which ones are missing when you add the component.

npx expo install react-native-reanimated react-native-gesture-handler @shopify/react-native-skia expo-haptics react-native-safe-area-context react-native-css

Install

npx @rslab/cli add pull-refresh-island

Usage

import { PullRefreshIsland } from '@/components/PullRefreshIsland';

<PullRefreshIsland onRefresh={async () => await reload()}>
  {/* scrollable content */}
</PullRefreshIsland>;

Props

PropTypeDefaultDescription
onRefresh() => Promise<void> | voidnoneCalled on release past the threshold.
thresholdnumber100Pull distance (pt) that triggers a refresh.
classNamestringnoneContainer classes (NativeWind).
contentContainerClassNamestringnoneClasses for the scroll content wrapper.

How it works

  • Metaballs. The island and the drop are plain black shapes on a Skia canvas, inside a layer that applies a Blur then a ColorMatrix that sharpens the blurred alpha back into a hard edge. When the shapes are close their blurred alphas overlap and survive the threshold: that overlap is the liquid neck, and it snaps on its own as the drop travels away.
  • Gesture. A Pan gesture runs simultaneously with the ScrollView and only accumulates at the top of the scroll, with progressive resistance. Past the threshold, release holds the drop in place, runs onRefresh, then springs everything back.
  • Dynamic Island detection. Island devices report a top inset of 59pt or more, and the island sits 48pt above the bottom of that inset. On devices without an island the drop drips from the top edge instead.

Tuning

  • Blur blur={6}: how far the liquid reaches, higher means thicker goo
  • GOO_MATRIX (40, -18): edge sharpness of the thresholded alpha
  • DROP_RADIUS: size of the detached drop
  • holdY: where the drop rests while refreshing

On this page