Marshall Gordon
Marshall Gordon

Reputation: 63

How to force an immediate redraw for a futurebuilder when state is changed?

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();
              }
            },
          )

Upvotes: 6

Views: 9961

Answers (3)

seif khelifi
seif khelifi

Reputation: 41

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!

Upvotes: -1

Rémi Rousselet
Rémi Rousselet

Reputation: 277037

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");
        }
      },
    );
  }
}

Upvotes: 8

Dhiraj Sharma
Dhiraj Sharma

Reputation: 4859

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
}

Upvotes: 9

Related Questions