ping_pong_refresh

pub version license

A Cupertino pull-to-refresh sliver with an animated ping-pong rally. Drop it into any CustomScrollView as a zero-dependency replacement for CupertinoSliverRefreshControl.

ping_pong_refresh demo


How it works

Pull down Armed Refreshing
Ball winds up between two paddles tracking your drag Haptic fires, paddles are fully open Paddles rally the ball back and forth until your callback completes
  • Drag — ball slides from left paddle to right as you pull, paddles rotate open
  • Armed — selection haptic fires the moment you hit the trigger threshold
  • Refreshing — ball bounces back and forth in a looping rally animation
  • Done — light haptic fires, animation stops, list springs back

Installation

Add to your pubspec.yaml:

dependencies:
  ping_pong_refresh: ^0.1.0

Then run:

flutter pub get

Basic usage

import 'package:ping_pong_refresh/ping_pong_refresh.dart';

CustomScrollView(
  slivers: [
    PingPongRefresh(
      onRefresh: () async {
        await myRepo.reload();
      },
    ),
    SliverList.builder(
      itemCount: items.length,
      itemBuilder: (context, i) => ListTile(title: Text(items[i])),
    ),
  ],
)

Keep the animation visible on fast data sources

Wrap your callback with runWithMinPingPongDuration to guarantee the rally plays for at least 2 seconds — otherwise it flashes past on cached or instant responses:

PingPongRefresh(
  onRefresh: () => runWithMinPingPongDuration(() async {
    await myRepo.reload();
  }),
)

Theming

Dark (default)

PingPongRefresh(
  theme: PingPongTheme(), // neon lime + electric blue
  onRefresh: () async { ... },
)

Light preset

PingPongRefresh(
  theme: PingPongTheme.light(), // green + blue on white
  onRefresh: () async { ... },
)

Fully custom

PingPongRefresh(
  theme: PingPongTheme(
    leftPaddleColor: Colors.orange,
    rightPaddleColor: Colors.purple,
    ballGradientColors: [Colors.white, Colors.orange],
    ballGradientStops: [0.4, 1.0],
    labelColor: Colors.grey,
    handleColor: Color(0xFFBCAAA4),
    handleCollarColor: Color(0xFF8D6E63),
  ),
  onRefresh: () async { ... },
)

Disable haptics

PingPongRefresh(
  enableHaptics: false,
  onRefresh: () async { ... },
)

API reference

PingPongRefresh

Parameter Type Default Description
onRefresh RefreshCallback required Called when the user triggers a refresh
theme PingPongTheme PingPongTheme() Visual configuration
enableHaptics bool true Haptic on arm + on complete

PingPongTheme

Property Type Default
leftPaddleColor Color Color(0xFFC0F500) — neon lime
rightPaddleColor Color Color(0xFF2792FF) — electric blue
ballGradientColors List<Color> white → lime
ballGradientStops List<double> [0.38, 0.78, 1.0]
labelColor Color Color(0xFFC4CAAC)
handleColor Color Color(0xFFCC9E64) — wood
handleCollarColor Color Color(0xFF9E7645) — dark wood

Helpers

Symbol Description
runWithMinPingPongDuration(action) Runs action but ensures at least kPingPongMinRefreshDuration (2s) elapses
kPingPongMinRefreshDuration Duration(seconds: 2)

Preview

ping_pong_refresh in action


Requirements

  • Flutter >=3.24.0
  • Dart >=3.5.0
  • Works on iOS and Android. The Cupertino pull gesture is native on iOS; on Android it overlays naturally inside a CustomScrollView.

License

MIT © Amr Sabbagh

Libraries

ping_pong_refresh
A Cupertino pull-to-refresh sliver with an animated ping-pong rally.