Bird
Raised Fist0
Fluttermobile~20 mins

Pull-to-refresh in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: PullToRefreshList
A screen that shows a list of items and allows the user to refresh the list by pulling down from the top.
Target UI
-------------------------
| PullToRefreshList      |
-------------------------
| Item 1                 |
| Item 2                 |
| Item 3                 |
| ...                    |
|                        |
-------------------------
Display a scrollable list of at least 5 items labeled 'Item 1', 'Item 2', etc.
Implement pull-to-refresh gesture to reload the list with updated items.
When refreshed, append ' (refreshed)' to each item label.
Show a loading spinner during refresh.
Starter Code
Flutter
import 'package:flutter/material.dart';

class PullToRefreshList extends StatefulWidget {
  @override
  State<PullToRefreshList> createState() => _PullToRefreshListState();
}

class _PullToRefreshListState extends State<PullToRefreshList> {
  List<String> items = List.generate(5, (index) => 'Item ${index + 1}');

  Future<void> _refresh() async {
    // TODO: Implement refresh logic
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('PullToRefreshList')),
      body: RefreshIndicator(
        onRefresh: _refresh,
        child: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            return ListTile(title: Text(items[index]));
          },
        ),
      ),
    );
  }
}
Task 1
Task 2
Task 3
Solution
Flutter
import 'package:flutter/material.dart';

class PullToRefreshList extends StatefulWidget {
  @override
  State<PullToRefreshList> createState() => _PullToRefreshListState();
}

class _PullToRefreshListState extends State<PullToRefreshList> {
  List<String> items = List.generate(5, (index) => 'Item ${index + 1}');

  Future<void> _refresh() async {
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      items = items.map((item) => '$item (refreshed)').toList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('PullToRefreshList')),
      body: RefreshIndicator(
        onRefresh: _refresh,
        child: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            return ListTile(title: Text(items[index]));
          },
        ),
      ),
    );
  }
}

This Flutter app shows a list of 5 items. The RefreshIndicator widget wraps the list and detects the pull-to-refresh gesture. When the user pulls down, the _refresh method runs, simulating a 2-second delay to mimic loading data. Then it updates the list by adding ' (refreshed)' to each item and calls setState to refresh the UI. The loading spinner appears automatically during the refresh.

Final Result
Completed Screen
-------------------------
| PullToRefreshList      |
-------------------------
| Item 1 (refreshed)     |
| Item 2 (refreshed)     |
| Item 3 (refreshed)     |
| Item 4 (refreshed)     |
| Item 5 (refreshed)     |
|                        |
-------------------------
User scrolls down from the top to trigger pull-to-refresh.
A circular loading spinner appears at the top.
After 2 seconds, the list updates to show '(refreshed)' appended to each item.
Stretch Goal
Add a button in the app bar to reset the list to original items without '(refreshed)'.
💡 Hint
Add an IconButton in AppBar actions that calls setState to reset the items list.

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