Returned future after dispose

Viewed 327

I need to show a snackbar when an api call returns. The problem is that I pop the route before this api call is done. So I get the following error:

[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.

This is my code:

ElevatedButton(
  onPressed: () async {

    final snackBar = SnackBar(
      behavior: SnackBarBehavior.fixed,
      duration: Duration(seconds: 2),
      content: Text('Done'),
    );

    apiCall().whenComplete(() => ScaffoldMessenger.of(context).showSnackBar(snackBar));
    Navigator.pop(context);
  },
  child: Text('Fetch data'),
),

How can I show a snackbar when the api call is done after the route is popped?

1 Answers

There are a few options for doing this. The simplest is probably to keep your current screen around until the API key and show some sort of indication that an operation is in progress (i.e. a CircularProgressIndicator). The next simplest is for the API call to be done from the page that called this one - i.e. in that page when you call Navigator.push(...), you can instead do let shouldDoCall = await Navigator.push(...) and then if (shouldDoCall == true) apiCall()..., and finally, in your ElevatedButton's on-pressed you could do Navigator.pop(context, true); if the button is pressed and false otherwise.

If neither of those is an option, you're probably going to want to implement some sort of api controller/manager which is responsible for these long-lived network requests - that's going to get more complicated though.

I can give you a very quick overview of one way to implement that, but the actual code is probably beyond the scope of this question. Using something like Provider, you could have a high-level ApiController Model that sat in your tree above your navigator. You'd be able to get access it to it from any page, and trigger the request. And in every page that is able to show messages, you could subscribe to an event stream from the same controller in the page's initState, and unsubscribe in the dispose method. If you want to get really fancy, you could even have the events queue instead of just being a dumb stream, which would mean that whenever a page next subscribed they would receive any events that had been queued up to be shown but not actually shown yet.

Related