Skip to content

Themes & Built-in Indicators

smart_refresher ships with 5 distinct visual themes for both the header (pull-to-refresh) and footer (load-more). Each theme is a self-contained widget that handles all refresh states automatically.


The default, platform-adaptive indicator. On Android it renders a CircularProgressIndicator; on iOS it uses a CupertinoActivityIndicator.

SmartRefresher(
header: const ClassicHeader(),
footer: const ClassicFooter(),
controller: _controller,
onRefresh: _onRefresh,
child: ListView.builder(...),
)
Property Type Default Description
refreshingText String "Refreshing..." Text shown during active refresh.
completeText String "Refresh Complete" Text shown on success.
failedText String "Refresh Failed" Text shown on failure.
idleText String "Pull down to refresh" Text shown at rest.
releaseText String "Release to refresh" Text shown when armed.

A Material Design 3 floating indicator that lifts off the page with an elevation shadow and uses your ThemeData.colorScheme.

SmartRefresher(
header: Material3Header(
elevation: 8.0, // Optional: controls card shadow depth
),
controller: _controller,
onRefresh: _onRefresh,
child: ListView.builder(...),
)

A pixel-perfect recreation of the iOS 17 native pull-to-refresh indicator, featuring a 12-spoke “tick” geometry and authentic haptic feedback via the HapticFeedback API.

SmartRefresher(
header: const iOS17Header(),
controller: _controller,
onRefresh: _onRefresh,
child: ListView.builder(...),
)

Enable threshold haptics globally via RefreshConfiguration:

RefreshConfiguration(
enableThresholdHaptic: true, // Vibrates when the user crosses the pull threshold
child: MaterialApp(...),
)

These themes use Flutter’s CustomPainter to create fluid, liquid-feeling animations as the user pulls:

  • WaterDropHeader: A droplet that stretches and snaps back.
  • BezierHeader: A wave-like curve that bends with the pull distance.
// WaterDrop feel
SmartRefresher(
header: const WaterDropHeader(),
controller: _controller,
onRefresh: _onRefresh,
child: ListView.builder(...),
)
// Bezier wave feel
SmartRefresher(
header: const BezierHeader(
bezierColor: Colors.blue,
),
controller: _controller,
onRefresh: _onRefresh,
child: ListView.builder(...),
)

A premium glassmorphism-style indicator that blurs the content beneath it. Perfect for editorial, photo-heavy, or modern-style apps.

SmartRefresher(
header: const GlassHeader(),
controller: _controller,
onRefresh: _onRefresh,
child: ListView.builder(...),
)

All footer types share a similar pattern. The most common are:

SmartRefresher(
enablePullUp: true,
footer: ClassicFooter(
loadingText: "Loading...",
noDataText: "No more items",
failedText: "Load failed. Tap to retry.",
),
controller: _controller,
onLoading: _onLoading,
child: ListView.builder(...),
)

Use a CustomFooter for a fully bespoke load-more indicator. The builder function receives the current LoadStatus:

SmartRefresher(
enablePullUp: true,
footer: CustomFooter(
builder: (context, mode) {
Widget body;
if (mode == LoadStatus.idle) {
body = const Text("Pull up to load more");
} else if (mode == LoadStatus.loading) {
body = const CircularProgressIndicator();
} else if (mode == LoadStatus.noData) {
body = const Text("You've reached the end 🎉");
} else {
body = const Text("Load failed. Tap to retry.");
}
return SizedBox(height: 55.0, child: Center(child: body));
},
),
controller: _controller,
onLoading: _onLoading,
child: ListView.builder(...),
)

Instead of configuring each SmartRefresher manually, set defaults at the root of your app:

RefreshConfiguration(
headerBuilder: () => const Material3Header(), // Default header for all refreshers
footerBuilder: () => const ClassicFooter(), // Default footer for all refreshers
headerTriggerDistance: 80.0,
footerTriggerDistance: 15.0,
child: MaterialApp(...),
)

For color/style theming, use SmartRefresherThemeData in your ThemeData.extensions. See the Theming reference for full details.