Pull-to-refresh lets users update content by dragging down on the screen. It feels natural and easy, like refreshing a page.
Pull-to-refresh in Flutter
Introduction
Syntax
Flutter
RefreshIndicator(
onRefresh: () async {
// Your refresh code here
},
child: ListView(
children: [
// Your list items
],
),
)The onRefresh function must return a Future<void>.
The child is usually a scrollable widget like ListView.
Examples
Flutter
RefreshIndicator(
onRefresh: () async {
print('Refreshing...');
},
child: ListView(
children: [Text('Item 1'), Text('Item 2')],
),
)Flutter
RefreshIndicator(
onRefresh: () async {
await Future.delayed(const Duration(seconds: 2));
},
child: ListView.builder(
itemCount: 5,
itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
),
)Sample App
This app shows a list of fruits. When you pull down and release, it waits 1 second and adds a new item with the current time. The list updates automatically.
Flutter
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: RefreshExample(), ); } } class RefreshExample extends StatefulWidget { const RefreshExample({super.key}); @override State<RefreshExample> createState() => _RefreshExampleState(); } class _RefreshExampleState extends State<RefreshExample> { List<String> items = ['Apple', 'Banana', 'Cherry']; Future<void> _refresh() async { await Future.delayed(const Duration(seconds: 1)); setState(() { items.add('New fruit at ${DateTime.now().toLocal().toIso8601String()}'); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Pull-to-refresh Example')), body: RefreshIndicator( onRefresh: _refresh, child: ListView.builder( itemCount: items.length, itemBuilder: (context, index) => ListTile( title: Text(items[index]), ), ), ), ); } }
Important Notes
Make sure the child widget is scrollable, or pull-to-refresh won't work.
You can customize the spinner color with the color property.
Test pull-to-refresh on a real device or emulator with touch support.
Summary
Pull-to-refresh lets users update content by dragging down on a list.
Use RefreshIndicator with an async onRefresh callback.
The child must be a scrollable widget like ListView.
Practice
1. What is the main purpose of using
RefreshIndicator in a Flutter app?easy
Solution
Step 1: Understand the role of RefreshIndicator
The RefreshIndicator widget provides a pull-to-refresh feature that triggers an update when the user drags down on a scrollable list.Step 2: Identify the correct use case
It is specifically designed to wrap scrollable widgets like ListView to refresh their content.Final Answer:
To enable pull-to-refresh functionality on a scrollable widget -> Option CQuick Check:
Pull-to-refresh = RefreshIndicator usage [OK]
Hint: RefreshIndicator is for pull-to-refresh on scrollable lists [OK]
Common Mistakes:
- Confusing RefreshIndicator with navigation widgets
- Using RefreshIndicator on non-scrollable widgets
- Thinking it creates buttons or spinners
2. Which of the following is the correct syntax to add a pull-to-refresh feature using
RefreshIndicator in Flutter?easy
Solution
Step 1: Recall RefreshIndicator parameters
The RefreshIndicator requires anonRefreshcallback and achildwidget that is scrollable.Step 2: Check correct parameter names and usage
RefreshIndicator(onRefresh: _refresh, child: ListView()) correctly usesonRefresh: _refreshandchild: ListView(). RefreshIndicator(child: ListView(), refresh: _refresh) uses wrong parameter namerefresh. RefreshIndicator(refreshCallback: _refresh, child: ListView()) usesrefreshCallbackwhich is incorrect. RefreshIndicator(onRefresh: _refresh()) calls_refresh()instead of passing the function.Final Answer:
RefreshIndicator(onRefresh: _refresh, child: ListView()) -> Option BQuick Check:
Correct parameters = RefreshIndicator(onRefresh: _refresh, child: ListView()) [OK]
Hint: Use onRefresh and child parameters exactly [OK]
Common Mistakes:
- Using wrong parameter names like refresh or refreshCallback
- Calling the refresh function instead of passing it
- Not providing a scrollable child widget
3. Given the code snippet below, what will happen when the user pulls down on the list?
RefreshIndicator(
onRefresh: () async {
await Future.delayed(Duration(seconds: 2));
},
child: ListView(
children: [Text('Item 1'), Text('Item 2')],
),
)medium
Solution
Step 1: Analyze the onRefresh callback
The onRefresh callback returns a Future that completes after a 2-second delay using Future.delayed.Step 2: Understand RefreshIndicator behavior
When the user pulls down, the RefreshIndicator shows a loading spinner until the Future completes, so it will show for 2 seconds then stop.Final Answer:
A loading spinner appears for 2 seconds, then stops -> Option AQuick Check:
Async delay = spinner visible 2 seconds [OK]
Hint: Async onRefresh delays spinner duration [OK]
Common Mistakes:
- Assuming immediate refresh without delay
- Thinking the app crashes without explicit return
- Believing nothing happens if no data changes
4. Identify the error in this Flutter code snippet that uses
RefreshIndicator:
RefreshIndicator(
onRefresh: () async {
// refresh logic
},
child: Column(
children: [Text('Item 1'), Text('Item 2')],
),
)medium
Solution
Step 1: Check the child widget type
The child of RefreshIndicator must be a scrollable widget like ListView or CustomScrollView. Column is not scrollable.Step 2: Understand the impact of non-scrollable child
Using Column will cause the pull-to-refresh gesture not to work because the widget does not scroll.Final Answer:
The child widget must be scrollable, Column is not scrollable -> Option AQuick Check:
Scrollable child required = The child widget must be scrollable, Column is not scrollable [OK]
Hint: RefreshIndicator child must scroll (ListView, not Column) [OK]
Common Mistakes:
- Thinking async is disallowed in onRefresh
- Expecting onRefresh to return boolean
- Believing any widget can be child
5. You want to implement pull-to-refresh on a list that fetches data from the internet. Which approach correctly updates the UI after refreshing?
class MyList extends StatefulWidget {
@override
State<MyList> createState() => _MyListState();
}
class _MyListState extends State<MyList> {
List<String> items = ['Old item'];
Future<void> _refresh() async {
final newData = await fetchDataFromInternet();
// What should you do here?
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _refresh,
child: ListView(
children: items.map((e) => Text(e)).toList(),
),
);
}
}hard
Solution
Step 1: Understand state update in StatefulWidget
To update the UI after fetching new data, you must call setState with the new data assigned to the state variable.Step 2: Apply setState in _refresh method
Inside _refresh, wrap the assignment of items = newData in setState to notify Flutter to rebuild the widget with updated data.Final Answer:
Call setState(() { items = newData; }) inside _refresh to update UI -> Option DQuick Check:
State update requires setState call [OK]
Hint: Use setState to update UI after async data fetch [OK]
Common Mistakes:
- Updating state variables without setState
- Returning data without updating state
- Using navigation calls instead of state update
Recommended
Ready to prove it?
Pass the test and this pattern moves unknown
10 questions ~5 mineasy hard
