How to implement Pull-to-Refresh in Flutter
(240 Articles)
In this tutorial, we’ll build a simple Flutter app that fetches a to-do list from an open API and displays it on the screen. We also implement a Pull-to-Refresh feature that lets the user refresh the content by making a pulling down gesture.

Here’s the API url used in the app (thanks to the Typicode team):
https://jsonplaceholder.typicode.com/todos
It simply contains a list of todos (tasks). A single todo looks like this:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Getting Started
1. Project setup
Create a new Flutter project by running this command:
flutter create app_example
2. Installing the HTTP package to fetch data from APIs.
Find the dependencies section in your pubspec.yaml and add http: ^0.12.2, like this:
dependencies:
flutter:
sdk: flutter
http: ^0.12.2
Then run:
flutter pub get
3. Build the skeleton
Remove all the default code in main.dart and add this “skeleton” code:
import 'package:flutter/material.dart';
import 'dart:convert';
// import the http package
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
title: 'Kindacode.com',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// The initial todos
List _todos = [];
// Call this when the user pull down the screen
Future<void> _loadData() async {
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kindacode.com'),
),
body: RefreshIndicator(
// trigger the _loadData function when the user pulls down
onRefresh: _loadData,
// Render the todos
child: ListView.builder(
itemCount: _todos.length,
itemBuilder: (BuildContext ctx, index) {
// The List Item will be added later
})),
);
}
}
Note: To implement Pull-to-Refresh, just wrap your widgets with a RefreshIndicator widget and configure the onRefresh property.
4. Loading data
Complete the _loadData function:
// Call this when the user pull down the screen
Future<void> _loadData() async {
final url = 'https://jsonplaceholder.typicode.com/todos';
try {
final http.Response response = await http.get(url);
final _loadedTodos = json.decode(response.body);
setState(() {
_todos = _loadedTodos;
});
} catch (err) {
throw err;
}
}
5. Render the list item
Add this code right after // The List Item will be added later, like this:
// The List Item will be added later
return Card(
margin: EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: ListTile(
// Render each todo
leading: Text(_todos[index]['id'].toString()),
title: Text(_todos[index]["title"]),
trailing: _todos[index]["completed"]
? Icon(
Icons.check_circle,
color: Colors.blue,
)
: Icon(
Icons.circle,
color: Colors.yellow,
),
),
);
6. The Final Code:
import 'package:flutter/material.dart';
import 'dart:convert';
// import the http package
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
title: 'Kindacode.com',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// The initial todos
List _todos = [];
// Call this when the user pull down the screen
Future<void> _loadData() async {
final url = 'https://jsonplaceholder.typicode.com/todos';
try {
final http.Response response = await http.get(url);
final _loadedTodos = json.decode(response.body);
setState(() {
_todos = _loadedTodos;
});
} catch (err) {
throw err;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kindacode.com'),
),
body: RefreshIndicator(
// trigger the _loadData function when the user pulls down
onRefresh: _loadData,
// Render the todos
child: ListView.builder(
itemCount: _todos.length,
itemBuilder: (BuildContext ctx, index) {
return Card(
margin: EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: ListTile(
// Render each todo
leading: Text(_todos[index]['id'].toString()),
title: Text(_todos[index]["title"]),
trailing: _todos[index]["completed"]
? Icon(
Icons.check_circle,
color: Colors.blue,
)
: Icon(
Icons.circle,
color: Colors.yellow,
),
),
);
})),
);
}
}
7. Check it out:
Conclusion
We have walked through an end-to-end example of implementing Pull-to-Refresh in a Flutter application with RefreshIndicator. If you would like to explore more interesting things about Flutter, take a look at the following articles: Load and display content from CSV files, Set an image Background for the entire screen, 3 Ways to create Random Colors in Flutter, Flutter Cupertino Button – Tutorial and Examples.
You can also check out our Flutter topic page or Dart topic page for the latest tutorials and examples.