Bird
Raised Fist0
Fluttermobile~5 mins

Pull-to-refresh in Flutter

Choose your learning style10 modes available
Introduction

Pull-to-refresh lets users update content by dragging down on the screen. It feels natural and easy, like refreshing a page.

When showing a list of news articles that can update with new stories.
In a chat app to load the latest messages.
On a social media feed to get new posts.
In a shopping app to refresh product availability.
When displaying weather info that changes often.
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
Simple pull-to-refresh that prints a message when triggered.
Flutter
RefreshIndicator(
  onRefresh: () async {
    print('Refreshing...');
  },
  child: ListView(
    children: [Text('Item 1'), Text('Item 2')],
  ),
)
Pull-to-refresh with a 2-second delay simulating data loading.
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]),
          ),
        ),
      ),
    );
  }
}
OutputSuccess
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/5)
1. What is the main purpose of using RefreshIndicator in a Flutter app?
easy
A. To navigate between different screens
B. To create a button for submitting forms
C. To enable pull-to-refresh functionality on a scrollable widget
D. To display a loading spinner on app startup

Solution

  1. 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.
  2. Step 2: Identify the correct use case

    It is specifically designed to wrap scrollable widgets like ListView to refresh their content.
  3. Final Answer:

    To enable pull-to-refresh functionality on a scrollable widget -> Option C
  4. Quick 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
A. RefreshIndicator(child: ListView(), refresh: _refresh)
B. RefreshIndicator(onRefresh: _refresh, child: ListView())
C. RefreshIndicator(refreshCallback: _refresh, child: ListView())
D. RefreshIndicator(onRefresh: _refresh())

Solution

  1. Step 1: Recall RefreshIndicator parameters

    The RefreshIndicator requires an onRefresh callback and a child widget that is scrollable.
  2. Step 2: Check correct parameter names and usage

    RefreshIndicator(onRefresh: _refresh, child: ListView()) correctly uses onRefresh: _refresh and child: ListView(). RefreshIndicator(child: ListView(), refresh: _refresh) uses wrong parameter name refresh. RefreshIndicator(refreshCallback: _refresh, child: ListView()) uses refreshCallback which is incorrect. RefreshIndicator(onRefresh: _refresh()) calls _refresh() instead of passing the function.
  3. Final Answer:

    RefreshIndicator(onRefresh: _refresh, child: ListView()) -> Option B
  4. Quick 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
A. A loading spinner appears for 2 seconds, then stops
B. The list will refresh immediately without delay
C. The app will crash due to missing return value
D. Nothing happens because onRefresh is empty

Solution

  1. Step 1: Analyze the onRefresh callback

    The onRefresh callback returns a Future that completes after a 2-second delay using Future.delayed.
  2. 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.
  3. Final Answer:

    A loading spinner appears for 2 seconds, then stops -> Option A
  4. Quick 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
A. The child widget must be scrollable, Column is not scrollable
B. The onRefresh callback must return a boolean
C. RefreshIndicator cannot have children widgets
D. The async keyword is not allowed in onRefresh

Solution

  1. Step 1: Check the child widget type

    The child of RefreshIndicator must be a scrollable widget like ListView or CustomScrollView. Column is not scrollable.
  2. 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.
  3. Final Answer:

    The child widget must be scrollable, Column is not scrollable -> Option A
  4. Quick 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
A. Return newData from _refresh without updating items
B. Just assign items = newData without setState
C. Call Navigator.pop(context) after fetching data
D. Call setState(() { items = newData; }) inside _refresh to update UI

Solution

  1. 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.
  2. 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.
  3. Final Answer:

    Call setState(() { items = newData; }) inside _refresh to update UI -> Option D
  4. Quick 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 strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard