Search code examples
flutterflutter-futurebuilder

Flutter FutureBuilder Does Not Wait When App Updates


Problem My FutureBuilder waits when app first runs but doesn't wait when app updates.

When my app finishes loading and I change to a different ToggleButton, the FutureBuilder starts to rerun immediately instead of waiting for getData() and it fully completes before getData() is finished and then when getData() is finally finished, FutureBuilder runs again.

This problem does not happen when the app first runs. When the app first runs, the FutureBuilder waits for getData() to complete before running.

I need FutureBuilder to wait for getData() to finish when a different button is pressed just like it does when the app first starts up.

Note: I removed as much unnecessary code as I could for readability. I can add more code if it will help.

Code:

class PriceScreenState extends State<PriceScreen> {
  String selectedCurrency = 'USD';
  String selectedGraphType = "1D";
      var isSelectedGraph = <bool>[true, false, false, false, false, false];

getData() async {
    isWaiting = true;
    try {
      Map graphData = await GraphData().getGraphData(
          selectedCurrency: selectedCurrency,
          selectedGraphType: selectedGraphType);
      isWaiting = false;
      setState(() {
        graphValues = graphData;
      });
    } catch (e) {
      print(e);
    }
  }

 @override
  void initState() {
    super.initState();
    futureData = getData();
  }

@override
  Widget build(BuildContext context) {
...(other code)...
ToggleButtons( ****************TOGGLEBUTTONS***********
                children: <Widget>[
                  Padding(
                    padding: EdgeInsets.symmetric(horizontal: 16.0),
                    child: Text('1D'),
                  ),
                 ...(more Buttons)...
                ],
                onPressed: (int index) {
                  setState(() {
                    for (int buttonIndex = 0;
                        buttonIndex < isSelectedGraph.length;
                        buttonIndex++) {
                      if (buttonIndex == index) {
                        isSelectedGraph[buttonIndex] = true;
                        selectedGraphType = graphType[buttonIndex];
                      } else {
                        isSelectedGraph[buttonIndex] = false;
                      }
                    }
                  });
                  getData();
                },
                isSelected: isSelectedGraph,
              ),
              Expanded(
                child: FutureBuilder( *************FUTUREBUILDER*********
                    future: futureData,
                    builder: (context, snapshot) {
                      if (graphValues.isEmpty) {
                        return new Container();
                      } else {
                        return Graph(graphValues);
                      }
                    }),
              )

Solution

  • As you are using a FutureBuilder you don't need to call setState anymore. Here is a possible rework of your code:

    Future<Map> futureData;
    
    Future<Map> getData() async {
       try {
          Map graphData = await GraphData().getGraphData(
             selectedCurrency: selectedCurrency,
             selectedGraphType: selectedGraphType,
          );
          return graphData;
       } catch (e) {
          throw Exception(e);
       }
    }
    
    @override
    void initState() {
       super.initState();
       futureData = getData();
    }
    
    @override
    Widget build(BuildContext context) {
       // Only coding the FutureBuilder for the example
       return FutureBuilder<Map>(
          future: futureData,
          builder: (context, snapshot) {
             // Future is still loading
             if (!snapshot.hasData)
                return CircularProgressIndicator();
             else if (snapshot.data.isEmpty)
                return Container();
             else
                return Graph(snapshot.data);
          },
       );
    }
    

    For your FutureBuilder to work correctly you need to return a value in your getData and use the snapshot variable.