'pull down to REFRESH in Flutter
My dashboard code looks like this,
Here I am doing get req in getReport method, I have added the RefreshIndicator in the code which when pulled down inside container should do the refresh, there I am calling my getData(), But I am not getting the refreshed content, I am adding my code below, let me know if anywhere I made a mistake.
below my dashboard.dart
class Window extends StatefulWidget {
  @override
  _WindowState createState() => _WindowState();
}
class _WindowState extends State<Window> {
  Future reportList;    
  @override
  void initState() {
    super.initState();
    reportList = getReport();
  }    
  Future<void> getReport() async {
    http.Response response =
        await http.get(reportsListURL, headers: {"token": "$token"});
    switch (response.statusCode) {
      case 200: 
        String reportList = response.body;
        var collection = json.decode(reportList);
        return collection;
      case 403:
          break;
      case 401:
        return null;
      default:
        return 1;
    }
  }
  getRefreshScaffold() {
    return Center(
      child: RaisedButton(
        onPressed: () {
          setState(() {
            reportList = getReport();
          });
        },
        child: Text('Refresh, Network issues.'),
      ),
    );
  }
  getDashBody(var data) {
    double maxHeight = MediaQuery.of(context).size.height;
    return Column(
      children: <Widget>[
        Container(
          height: maxHeight - 800,
        ),
        Container(
          margin: new EdgeInsets.all(0.0),
          height: maxHeight - 188,
          child: new Center(
          child: new RefreshIndicator(          //here I am adding the RefreshIndicator
          onRefresh:getReport,                  //and calling the getReport() which hits the get api
          child: createList(context, data),
          ),),
        ),
      ],
    );
  }
  Widget createList(BuildContext context, var data) {
    Widget _listView = ListView.builder(
      itemCount: data.length,
      itemBuilder: (context, count) {
        return createData(context, count, data);
      },
    );
    return _listView;
  }
  createData(BuildContext context, int count, var data) {
    var metrics = data["statistic_cards"].map<Widget>((cardInfo) {
      var cardColor = getColorFromHexString(cardInfo["color"]);
      if (cardInfo["progress_bar"] != null && cardInfo["progress_bar"]) {
        return buildRadialProgressBar(
          context: context,
          progressPercent: cardInfo["percentage"],
          color: cardColor,
          count: cardInfo["value"],
          title: cardInfo["title"],
        );
      } else {
        return buildSubscriberTile(context, cardInfo, cardColor);
      }
    }).toList();
    var rowMetrics = new List<Widget>();
    for (int i = 0; i < metrics.length; i += 2) {
      if (i + 2 < metrics.length)
        rowMetrics.add(Row(children: metrics.sublist(i, i + 2)));
      else
        rowMetrics.add(Row(children: [metrics[metrics.length - 1], Spacer()]));
    }
    return SingleChildScrollView(
      child: LimitedBox(
        //  maxHeight: MediaQuery.of(context).size.height / 1.30,
        child: Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: rowMetrics,
        ),
      ),
    );
  }
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: reportList,
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.waiting:
          case ConnectionState.active:
            return Center(
              child: CircularProgressIndicator(),
            );
          case ConnectionState.done:
            var data = snapshot.data;
            if (snapshot.hasData && !snapshot.hasError) {
              return getDashBody(data);
            } else if (data == null) {
              return Center(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Text("Timeout! Log back in to continue"),
                    Padding(
                      padding: EdgeInsets.all(25.0),
                    ),
                    RaisedButton(
                      onPressed: () {
                        setState(() {
                          token = null;
                        });
                        Navigator.of(context).pushReplacement(
                          CupertinoPageRoute(
                              builder: (BuildContext context) => LoginPage()),
                        );
                      },
                      child: Text('Login Again!'),
                    ),
                  ],
                ),
              );
            } else {
              getRefreshScaffold();
            }
        }
      },
    );
  }
}
							
						Solution 1:[1]
Basic Example
Below is a State class of a StatefulWidget, where:
- a 
ListViewis wrapped in aRefreshIndicatorwordsstate variable is its data source
 onRefreshcalls_pullRefreshfunction to update ListView_pullRefreshis an async function, returning nothing (aFuture<void>)- when 
_pullRefresh's long running data request completes,wordsmember/state variable is updated in asetState()call to rebuildListViewto display new data 
import 'package:english_words/english_words.dart';
class _PullToRefreshPageState extends State<PullToRefreshPage> {
  List<WordPair> words = generateWordPairs().take(5).toList();
  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: _pullRefresh,
      child: ListView.builder(
        itemCount: words.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(words[index].asPascalCase),
          );
        },),
    );
  }
  Future<void> _pullRefresh() async {
    List<WordPair> freshWords = await WordDataSource().getFutureWords(delay: 2);
    setState(() {
      words = freshWords;
    });
    // why use freshWords var? https://stackoverflow.com/a/52992836/2301224
  }
}
class WordDataSource {
  Future<List<WordPair>> getFutureWords({int size = 5, int delay = 5}) async {
    await Future.delayed(Duration(seconds: delay));
    return generateWordPairs().take(5).toList();
  }
}
Notes
- If your async 
onRefreshfunction completes very quickly, you may want to add anawait Future.delayed(Duration(seconds: 2));after it, just so the UX is more pleasant. - This gives time for the user to complete a swipe / pull down gesture & for the refresh indicator to render / animate / spin indicating data has been fetched.
 
FutureBuilder Example
Here's another example, using a FutureBuilder, which is common when fetching data from a Database or HTTP source
class _PullToRefreshFuturePageState extends State<PullToRefreshPage> {
  Future<List<WordPair>> futureWords;
  @override
  void initState() {
    super.initState();
    futureWords = WordDataSource().getFutureWords(delay: 2);
  }
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<WordPair>>(
      //initialData: [],
      future: futureWords,
      builder: (context, snapshot) {
        return RefreshIndicator(
          child: _listView(snapshot),
          onRefresh: _pullRefresh,
        );
      },
    );
  }
  Widget _listView(AsyncSnapshot snapshot) {
    if (snapshot.hasData) {
      return ListView.builder(
        itemCount: snapshot.data.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(snapshot.data[index].asPascalCase),
          );
        },);
    }
    else {
      return Center(
        child: Text('Loading data...'),
      );
    }
  }
  Future<void> _pullRefresh() async {
    List<WordPair> freshFutureWords = await WordDataSource().getFutureWords(delay: 2);
    setState(() {
      futureWords = Future.value(freshFutureWords);
    });
  }
}
Notes
getFutureWords()function is the same as in the Basic Example above, but the data is wrapped in aFuture.value()since FutureBuilder expects aFuture- according to Rémi, Collin & other Dart/Flutter demigods it's good practice to update Stateful Widget member variables inside 
setState()(futureWordsin FutureBuilder example &wordsin Basic example), after its long running async data fetch functions have completed. - if you try to make setState 
async, you'll get an exception - updating member variables outside of 
setStateand having an emptysetStateclosure, may result in hand-slapping / code analysis warnings in the future 
Solution 2:[2]
Not sure about futures, but for refresh indicator you must return a void so Use something like
RefreshIndicator(
                onRefresh: () async  {
                  await getData().then((lA) {
                    if (lA is Future) {
                      setState(() {
                        reportList = lA;
                      });
                      return;
                    } else {
                      setState(() {
                       //error
                      });
                      return;
                    }
                  });
                  return;
                },
Try this and let me know!
EDIT:
Well, then just try this inside you refresh method
          setState(() {
            reportList = getReport();  
          });
          return reportList;
    					Solution 3:[3]
Try this:
onRefresh: () {
  setState(() {});
}}
instead of onRefresh:getReport
reportList field is Future which returns its value once. So, when you call getReport again it changes nothing. Actually, more correctly it'll be with Stream and StreamBuilder instead of Future and FutureBuilder. But for this code it can be shortest solution
Solution 4:[4]
Easy method: you can just use Pull Down to Refresh Package - https://pub.dev/packages/pull_to_refresh
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source | 
|---|---|
| Solution 1 | Baker | 
| Solution 2 | |
| Solution 3 | |
| Solution 4 | Mano Haran | 
