whenComplete() method not working as expected - Flutter Async

In my Flutter app, I am trying to run some code after an async function is completed by using the whenComplete() method. The problem is the code in my whenComplete() method is getting executed even before the async function is finished.

I have tried using the then() method also and that is also producing the same result.

This is the init function in which I am calling that async function:

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

    FirebaseAuth.instance.currentUser().then((user) {
      setState(() {
        mUid = user.uid;

        getUserName();
        getUserHomes().whenComplete(() {
          print(mUserHomes);
          setState(() {
            dropdownVal = mUserHomes[0];
          });
        });
      });
    }).catchError((e) {
      print(e);
    });
  }

This is the function body for the async function:

Future getUserHomes() async {
    CollectionReference ref = Firestore.instance
        .collection('users')
        .document(mUid)
        .collection('accessibleHomes');
    QuerySnapshot eventsQuery = await ref.getDocuments();

    eventsQuery.documents.forEach((document) {
      DocumentReference homeReference = document["homeReference"];

      getHomeDevices("home", homeReference).whenComplete(() {
        setState(() {});
      });

      homeReference.get().then((DocumentSnapshot ds) {
        mUserHomes.add(ds["alias"].toString());
      });
    });
  }

The console output on running the app is:

Performing hot restart...
Syncing files to device ASUS Z01RD...
Restarted application in 2,521ms.
I/flutter ( 5718): []
E/flutter ( 5718): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty: 0
E/flutter ( 5718): #0      List.[] (dart:core-patch/growable_array.dart:145:60)
E/flutter ( 5718): #1      _TabOneState.initState.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:smart_switch/home/tabs/tab_one/tab_one_content.dart:52:37)
E/flutter ( 5718): #2      State.setState (package:flutter/src/widgets/framework.dart:1117:30)
E/flutter ( 5718): #3      _TabOneState.initState.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:smart_switch/home/tabs/tab_one/tab_one_content.dart:51:11)
E/flutter ( 5718): #4      _rootRun (dart:async/zone.dart:1120:38)
E/flutter ( 5718): #5      _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter ( 5718): #6      _FutureListener.handleWhenComplete (dart:async/future_impl.dart:150:18)
E/flutter ( 5718): #7      Future._propagateToListeners.handleWhenCompleteCallback (dart:async/future_impl.dart:609:39)
E/flutter ( 5718): #8      Future._propagateToListeners (dart:async/future_impl.dart:665:37)
E/flutter ( 5718): #9      Future._complete (dart:async/future_impl.dart:473:7)
E/flutter ( 5718): #10     _SyncCompleter.complete (dart:async/future_impl.dart:51:12)
E/flutter ( 5718): #11     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:28:18)
E/flutter ( 5718): #12     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:294:13)
E/flutter ( 5718): #13     _TabOneState.getUserHomes (package:smart_switch/home/tabs/tab_one/tab_one_content.dart)
E/flutter ( 5718): #14     _asyncThenWrapperHelper.<anonymous closure> (dart:async-patch/async_patch.dart:77:64)
E/flutter ( 5718): #15     _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter ( 5718): #16     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter ( 5718): #17     _FutureListener.handleValue (dart:async/future_impl.dart:126:18)
E/flutter ( 5718): #18     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:639:45)
E/flutter ( 5718): #19     Future._propagateToListeners (dart:async/future_impl.dart:668:32)
E/flutter ( 5718): #20     Future._complete (dart:async/future_impl.dart:473:7)
E/flutter ( 5718): #21     _SyncCompleter.complete (dart:async/future_impl.dart:51:12)
E/flutter ( 5718): #22     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:28:18)
E/flutter ( 5718): #23     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:294:13)
E/flutter ( 5718): #24     Query.getDocuments (package:cloud_firestore/src/query.dart)
E/flutter ( 5718): #25     _asyncThenWrapperHelper.<anonymous closure> (dart:async-patch/async_patch.dart:77:64)
E/flutter ( 5718): #26     _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter ( 5718): #27     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter ( 5718): #28     _FutureListener.handleValue (dart:async/future_impl.dart:126:18)
E/flutter ( 5718): #29     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:639:45)
E/flutter ( 5718): #30     Future._propagateToListeners (dart:async/future_impl.dart:668:32)
E/flutter ( 5718): #31     Future._complete (dart:async/future_impl.dart:473:7)
E/flutter ( 5718): #32     _SyncCompleter.complete (dart:async/future_impl.dart:51:12)
E/flutter ( 5718): #33     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:28:18)
E/flutter ( 5718): #34     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:294:13)
E/flutter ( 5718): #35     MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart)
E/flutter ( 5718): #36     _asyncThenWrapperHelper.<anonymous closure> (dart:async-patch/async_patch.dart:77:64)
E/flutter ( 5718): #37     _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter ( 5718): #38     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter ( 5718): #39     _FutureListener.handleValue (dart:async/future_impl.dart:126:18)
E/flutter ( 5718): #40     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:639:45)
E/flutter ( 5718): #41     Future._propagateToListeners (dart:async/future_impl.dart:668:32)
E/flutter ( 5718): #42     Future._complete (dart:async/future_impl.dart:473:7)
E/flutter ( 5718): #43     _SyncCompleter.complete (dart:async/future_impl.dart:51:12)
E/flutter ( 5718): #44     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:28:18)
E/flutter ( 5718): #45     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:294:13)
E/flutter ( 5718): #46     MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart)
E/flutter ( 5718): #47     _asyncThenWrapperHelper.<anonymous closure> (dart:async-patch/async_patch.dart:77:64)
E/flutter ( 5718): #48     _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter ( 5718): #49     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter ( 5718): #50     _FutureListener.handleValue (dart:async/future_impl.dart:126


So in the init() function where I am calling the async function getUserHomes I am trying to set the dropdownVal variable to the first value of the list mUserHomes which is fetched from Firestore DB. The list is printed as [] in the console which means empty and but if I print the list in later stages it is getting printed with the values.


Solution 1:

Your getUserHomes function does a bunch of asynchronous work in the foreach callback that it doesn't wait for, so getUserHomes completes its returned Future almost immediately after ref.getDocuments finishes.

If you want to wait for all work in getUserHomes to finish, you could do something like:

Future<void> getUserHomes() async {
  CollectionReference ref = Firestore.instance
      .collection('users')
      .document(mUid)
      .collection('accessibleHomes');
  QuerySnapshot eventsQuery = await ref.getDocuments();

  final waitList = <Future<void>>[];

  for (var document in eventsQuery.documents) {
    DocumentReference homeReference = document["homeReference"];

    waitList.add(getHomeDevices("home", homeReference));
    waitList.add(
      homeReference.get().then((DocumentSnapshot ds) {
        mUserHomes.add(ds["alias"].toString());
      });
    );
  }

  await Future.wait(waitList);
}