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(...),
)
PropertyTypeDefaultDescription
refreshingTextString"Refreshing..."Text shown during active refresh.
completeTextString"Refresh Complete"Text shown on success.
failedTextString"Refresh Failed"Text shown on failure.
idleTextString"Pull down to refresh"Text shown at rest.
releaseTextString"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.