Flutter – whenComplete() not working as expected when using Providers

Issue

I’m trying to display a loading while doing an API Request and when finished to show the list with the response or a custom widget to show a message(EmptyListWidget). The problem is that the whenComplete() method is being executed before the async function is finished.

I also tried using then() and using FutureBuilder but I also can’t make it work using Provider (allways returns null).

If someone could help, I would really appreciate it.. thanks 🙂

My List Widget:

class _AbsencesListState extends State<AbsencesList> {
  bool _isLoading = false;
  bool _isInit = true;

  @override
    void didChangeDependencies() {
    super.didChangeDependencies();
    if (_isInit) {
      setState(() => _isLoading = true);
      Provider.of<AbsencesTypes>(context, listen: false)
          .getAbsencesTypes(widget.ctx)
          .whenComplete(() {
        setState(() => _isLoading = false);
      });
      _isInit = false;
    }
  }
  
  @override
  Widget build(BuildContext context) {
    final absences = Provider.of<Absences>(context).items;
    return Stack(
      children: [
        _isLoading
            ? const Center(child: CircularProgressIndicator())
            : absences.length > 0
                ? Container()
                : EmptyListWidget(ListType.InconsistenciesList),
        ListView.builder(
          itemBuilder: (_, index) {
            return GestureDetector(
              onTap: () {},
              child: Card(
                elevation: 2.0,
                child: ListTile(
                  leading: CircleAvatar(
                    child: const Icon(Icons.sick),
                    backgroundColor: Theme.of(context).accentColor,
                    foregroundColor: Colors.white,
                  ),
                  title: Padding(
                    padding: const EdgeInsets.only(top: 3),
                    child: Text(absences[index].absenceType.name),
                  ),
                  subtitle: Text(
                    absences[index].firstDate
                  ),
                ),
              ),
            );
          },
          itemCount: absences.length,
        )
      ],
    );
  }
}

The async function:

class AbsencesTypes with ChangeNotifier {
  List<AbsenceType> _absencesTypesList = [];

  List<AbsenceType> get items {
    return [..._absencesTypesList];
  }

  void emptyAbsencesTypeList() {
    _absencesTypesList.clear();
  }

  Future<void> getAbsencesTypes(BuildContext context) async {
    SharedPreferences _prefs = await SharedPreferences.getInstance();
    String token = _prefs.getString(TOKEN_KEY);
    http.get(
      API_URL,
      headers: {"Authorization": token},
    ).then(
      (http.Response response) async {
        if (response.statusCode == 200) {
          final apiResponse = json.decode(utf8.decode(response.bodyBytes));
          final extractedData = apiResponse['content'];
          final List<AbsenceType> loadedAbsencesTypes = [];
          for (var absenceType in extractedData) {
            loadedAbsencesTypes.add(
              AbsenceType(
                id: absenceType["id"],
                name: absenceType["name"].toString(),
                code: absenceType["code"].toString(),
                totalAllowedDays: absenceType["totalAllowedDays"],
              ),
            );
          }
          _absencesTypesList = loadedAbsencesTypes;
        } else if (response.statusCode == 401) {
          Utility.showToast(
              AppLocalizations.of(context).translate("expired_session_string"));
          Utility.sendUserToLogin(_prefs, context);
        }
        notifyListeners();
      },
    );
  }
}

Solution

Your problem here is probably that you’re calling http.get without awaiting for it’s result.
The getAbsencesTypes returns the Future<void> as soon as the http.get method is executed, without waiting for the answer, and it results in your onComplete method to be triggered.

A simple fix would be to add the await keyword before the http.get, but you could do even better.

In your code, you’re not fully using the ChangeNotifierProvider which could solve your problem. You should check the Consumer class which will be pretty useful for you here, but since it’s not your initial question I won’t go more in depth on this subject.

Answered By – FDuhen

Answer Checked By – Mary Flores (FlutterFixes Volunteer)

Leave a Reply

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