6

When I change the state upon which a specific futurebuilder widget relies, the widget is not redrawn until the async task completes. I want the widget to be redrawn immediately once the state changes. Below is the code for the futurebuilder:

child: new FutureBuilder(
            future: _api.getListOfTourneys(searchTerm, filterChecks, pageNum),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                if(!(snapshot.data is List)){
                  return new TourneyItem(snapshot.data['tournament']);
                }
                return new Expanded(
                    child: ListView.builder(
                  shrinkWrap: false,
                  scrollDirection: Axis.vertical,
                  itemCount: snapshot.data.length,
                  itemBuilder: (BuildContext context, int index) {
                    return TourneyItem(snapshot.data[index]);
                  },
                ));
              } else if (snapshot.hasError) {
                return new Text("No results found");
              } else {
                return new CircularProgressIndicator();
              }
            },
          )
Jacob Phillips
  • 8,841
  • 3
  • 51
  • 66
  • Can you include an example of changing state? Is `TourneyItem` a StatefulWidget? – Jacob Phillips Jun 22 '18 at 04:16
  • The futurebuilder is a child of a StatefulWidget. An example of changing the stae would be changing the state variables `searchTerm, filterChecks, pageNum` that the future function uses as parameters. – Marshall Gordon Jun 23 '18 at 17:39

3 Answers3

9

I think FutureBuilder only builds once at start then everytime async task is compelted. So when we navigate to screen FutureBuilder is build at start then when async task is complete, then for other calls (except first) FutureBuilder only builds after async task is completed.

I also needed rebuild FutureBuilder at start of async call, I solved this issue by using snapshot.connectionState

Insider builder of FutureBuilder

if(snapshot.connectionState == ConnectionState.done){
    if(snapshot.hasData){
        //Show data here
    }else{
       //Show error here
    }
}else{
    //Show progress
}
Dhiraj Sharma
  • 4,364
  • 24
  • 25
  • I don't know if this is a bug in FutureBuilder but apparently hasData and hasError flags are not reset, so you indeed need to use snapshot.connectionState as mentioned in this answer. See also: https://stackoverflow.com/a/61436129/668230 – Zsolt Apr 27 '20 at 11:02
8

You can't do that using FutureBuilder. If you want to build more than "initial" + "done" don't use FutureBuilder.

Instead you can use a Stream and StreamBuilder by submitting your future result to the stream.

class Foo extends StatefulWidget {
  @override
  _FooState createState() => _FooState();
}

class _FooState extends State<Foo> {
  StreamController streamController;

  @override
  void initState() {
    load();
    super.initState();
  }

  load() async {
    streamController.add("Loading");
    await Future.delayed(Duration(seconds: 1));
    streamController.add("Done");
  }

  @override
  void didUpdateWidget(Foo oldWidget) {
    if (somethingChanged) {
      load();
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
    void dispose() {
      streamController.close();
      super.dispose();
    }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<String>(
      stream: streamController.stream,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return new Text(snapshot.data);
        } else if (snapshot.hasError) {
          return new Text("Error");
        } else {
          return new Text("Nothing");
        }
      },
    );
  }
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
-1

this a bit late but it worked for me just replace the screen with the same screen

Navigator.pushReplacement(context,
    MaterialPageRoute(
        builder: (BuildContext context) =>
           *SamePage*()
    )
);

don't use this method inside a showdialog to avoid backing on the same page twice!

seif khelifi
  • 45
  • 1
  • 4