Infinite loop on using FutureBuilder with API call

Issue

I am trying to populate my ListView with the result from an API. The API call must take place after the values have been retrieved from Shared Preference. However on execution my function for API call runs an infinite loop and the UI doesn’t render. I tracked this behaviour through debug statements.

The circular indicator that should be shown when Future builder is building UI is also not showing.

How can I resolve this?

My code:



class _MyHomePageState extends State<MyHomePage>{
  @override MyHomePage get widget => super.widget;

  String userID = "";
  String authID = "";


  //Retrieving values from Shared Preferences

  Future<List<String>> loadData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();

    List<String> l= new List<String>();

    if(prefs.getString("ID") == null){
      l.add("null");
    }
    else{
      l.add(prefs.getString("ID"));
    }

    if(prefs.getString("authID") == null){
      l.add("null");
    }
    else{
      l.add(prefs.getString("authID"));
    }

    return l;
  }

//Setting values retrieved from Shared Pref
  setData() async{
    await loadData().then((value) {
      setState(() {
        userID = value[0];
        print('the user ID is' + userID);
        authID = value[1];
        print('the authID is' + authID);
      });
     // getAllTasks(userID, authID);
    });
    print("Set data execution completed ");
  }


  //FUNCTION to use values from Shared Pref and make API Call 
  Future<List<Task>> getAllTasks() async{

     await setData();
    //Waiting for Set Data to complete

    print('Ive have retrived the values ' + userID + authID );

    List<Task> taskList;

    await getTasks(userID, authID, "for_me").then((value){

      final json = value;
      if(json!="Error"){

        Tasks tasks = tasksFromJson(json); //of Class Tasks
        taskList = tasks.tasks;  //getting the list of tasks from class
      }
    });

    if(taskList != null) return taskList;
    else {
      print('Tasklist was null ');
      throw new Exception('Failed to load data ');
    }

  }

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

 @override
  Widget build(BuildContext context){

   _signedOut(){
     widget.onSignedOut();
   }

//To CREATE LIST VIEW 
   Widget createTasksListView(BuildContext context, AsyncSnapshot snapshot) {
     var values = snapshot.data;
     return ListView.builder(
       itemCount: values == null ? 0 : values.length,
       itemBuilder: (BuildContext context, int index) {

         return values.isNotEmpty ? Ink(....
         ) : CircularProgressIndicator();
       },
     );
   }


//MY COLUMN VIEW 

   Column cardsView = Column(
      children: <Widget>[
      ....
        Expanded(
          child: FutureBuilder(
              future: getAllTasks(),
              initialData: [],
              builder: (context, snapshot) {
                return createTasksListView(context, snapshot);
              }),
        ),
      ],
   );


   return Scaffold(

     body:  cardsView,
     );
 }
}

Instead of being called once… my setData function is being called repeatedly.. How can I resolve this..please help

Solution

You’re creating Future object on every rebuild of the widget. And since you’re calling setState inside your setData method, it triggers a rebuild recursively.

To solve this problem you have to keep a reference to the Future object. And use that reference for the FutureBuilder then it can understand that it is the previously used one.

E.g:

Future<List<Task>> _tasks;
@override
  void initState() {
    _tasks = getAllTasks();
    super.initState();
  }

And in your widget tree use it like that:

Expanded(
          child: FutureBuilder(
              future: _tasks,
              initialData: [],
              builder: (context, snapshot) {
                return createTasksListView(context, snapshot);
              }),
        ),

Answered By – Gunhan

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *