Introduction
Implementing pull-to-refresh in Flutter enhances user experience by allowing users to reload content with a simple swipe gesture. In Flutter, the RefreshIndicator widget makes it straightforward to add pull-to-refresh Flutter functionality to scrollable views. This tutorial walks you through setting up Flutter pull to refresh using RefreshIndicator, handling the refresh logic, and customizing the indicator’s appearance—all in a few concise steps.
Setting up the project
Start with a new or existing Flutter app. Ensure you have Flutter installed (at least version 2.0). In your main Dart file, import material.dart:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
Inside MyApp, set up a basic MaterialApp and a stateful widget for your home screen. You’ll need a ListView to demonstrate data refreshing.
Adding RefreshIndicator
Wrap your ListView (or any scrollable widget) with RefreshIndicator. This provides the pull-to-refresh Flutter behavior:
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> items = List.generate(20, (i) => 'Item ${i + 1}');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Flutter pull to refresh')),
body: RefreshIndicator(
onRefresh: _handleRefresh,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: items.length,
itemBuilder: (_, index) => ListTile(title: Text(items[index])),
),
),
);
}
}Key points:
• AlwaysScrollableScrollPhysics ensures the pull gesture works even when content isn’t scrollable.
• The onRefresh callback returns a Future to signal completion.
Handling refresh logic
Define the _handleRefresh function to simulate fetching new data or calling an API. Update the state when data arrives:
Future<void> _handleRefresh() async {
await Future.delayed(Duration(seconds: 2));
setState(() {
items = List.generate(20, (i) => 'Refreshed Item ${i + 1}');
});
}In a real app, replace the delay with an HTTP request or Firebase query. The RefreshIndicator displays a circular progress indicator until the Future completes. This straightforward approach covers most Flutter pull to refresh use cases.
Customizing the indicator
RefreshIndicator supports customization parameters:
• color – the spinner’s color
• backgroundColor – the circle’s background
• displacement – how far the list must be pulled before triggering
Example:
RefreshIndicator(
color: Colors.white,
backgroundColor: Colors.blue,
displacement: 50,
onRefresh: _handleRefresh,
child: ,
)
Adjust these values to match your app’s theme. The spinner appears at the top of the list and follows Material Design guidelines, ensuring consistency across platforms.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
You’ve now learned to implement pull-to-refresh in Flutter using RefreshIndicator. You set up a scrollable ListView, added the RefreshIndicator wrapper, handled asynchronous data fetching, and customized the spinner to fit your app’s style. This pattern scales to more complex scenarios, such as infinite scrolling or combined refresh-and-load-more workflows.