Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reload data when using FutureBuilder

I am loading data when widget is loading like the code below. Once the UI is fully loaded, I like to add one refresh button to reload the data again.

How can I refresh the view ?

  class _MyHomePageState extends State<MyHomePage> {

      @override
      Widget build(BuildContext context) {
        var futureBuilder = new FutureBuilder(
          future: _getData(),
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.none:
              case ConnectionState.waiting:
                return new Text('loading...');
              default:
                if (snapshot.hasError)
                  return new Text('Error: ${snapshot.error}');
                else
                  return createListView(context, snapshot);
            }
          },
        );

        return new Scaffold(
          appBar: new AppBar(
            title: new Text("Home Page"),
          ),
          body: futureBuilder,
        );
      }

      Future<List<String>> _getData() async {
        var values = new List<String>();

        await new Future.delayed(new Duration(seconds: 5));

        return values;
      }

      Widget createListView(BuildContext context, AsyncSnapshot snapshot) {

      }
    }
like image 673
fifa chapman Avatar asked Nov 06 '18 10:11

fifa chapman


People also ask

What is a FutureBuilder?

FutureBuilder is a Widget that will help you to execute some asynchronous function and based on that function's result your UI will update. FutureBuilder is Stateful by nature i.e it maintains its own state as we do in StatefulWidgets. Syntax: FutureBuilder( Key key, this. future, this.

What are the similarities and differences of future and stream?

Both StreamBuilder and FutureBuilder have the same behavior: They listen to changes on their respective object. And trigger a new build when they are notified of a new value. So in the end, their differences are how the object they listen to works. Future is like Promise in JS or Task in c#.


3 Answers

Widget createListView(BuildContext context, AsyncSnapshot snapshot) {
  RaisedButton button = RaisedButton(
    onPressed: () {
      setState(() {});
    },
    child: Text('Refresh'),
  );
  //.. here create widget with snapshot data and with necessary button
}
like image 149
Andrey Turkovsky Avatar answered Oct 18 '22 21:10

Andrey Turkovsky


I did a deep dive into this and it's not that difficult. The builder is properly rebuilt on changing the future (if you trigger the change with setState). Problem is, the hasData and hasError aren't reset until the response is back. But we can use connectionState instead.

final builder = FutureBuilder(
    future: _future,
    builder: (context, snapshot) {
      if (snapshot.connectionState != ConnectionState.done) {
        return _buildLoader();
      }
      if (snapshot.hasError) {
        return _buildError();
      }
      if (snapshot.hasData) {
        return _buildDataView();
      }     
      return _buildNoData();
});

Here's a post on the issue and a linked repo showing the issue and solution: https://www.greycastle.se/reloading-future-with-flutter-futurebuilder/

like image 44
Almund Avatar answered Oct 18 '22 21:10

Almund


what i did an it worked for me, is to call the future function again in setState(). in your example it will looks like this.

first you assign your _getData() future function to a variable (_myData) with the same return type, after that, you can override it's value in setState() that will rebuild the UI and therefor run the future again.

in code it will looks like this.(from you example):

class _MyHomePageState extends State<MyHomePage> {

Future<List<String>>  _myData = _getData(); //<== (1) here is your Future

@override
      Widget build(BuildContext context) {
        var futureBuilder = new FutureBuilder(
          future: _myData; //<== (2) here you provide the variable (as a future)
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.none:
              case ConnectionState.waiting:
                return new Text('loading...');
              default:
                if (snapshot.hasError)
                  return Column(
                  children: [
                    Icon(Icons.error),
                    Text('Failed to fetch data.'),
                    RaisedButton(
                      child: Text('RETRY'), 
                      onPressed: (){
                        setState(){
                            _myData = _getData(); //<== (3) that will trigger the UI to rebuild an run the Future again
                        }
                      },
                    ),
                  ],
                );
                else
                  return createListView(context, snapshot);
            }
          },
        );

        return new Scaffold(
          appBar: new AppBar(
            title: new Text("Home Page"),
          ),
          body: futureBuilder,
        );
      }
like image 21
korchix Avatar answered Oct 18 '22 20:10

korchix