Search code examples
flutterflutter-providerriverpodflutter-stateflutter-http

How to handle navigation while an HTTP request is made in Flutter?


In my Flutter App, there are 2 screens called "home screen" and "data screen". In data screen I am making a GET Http request to a server and after if request is successful, I need to change some state using riverpod providers.

Data screen http request and provider code:

await Future.delayed(const Duration(seconds: 3));

var jsonBody = await HttpService.httpService.getRequest(
   path: Uri.parse("$address/getData"), 
   requestHeaders: {}
);

if((jsonBody["status"] as String).compareTo("OK") == 0 && mounted){       
   ref.read(dataProvider.notifier).setData(jsonBody["data"]);  
}

Here, if user closes the data screen (pops it off from nav stack) while http request is loading, the context of data screen widget is destroyed, so flutter gives me an error and says "looking up for a deactivated widget's ancestor is unsafe"

E/flutter (11326): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter (11326): At this point the state of the widget's element tree is no longer stable.
E/flutter (11326): 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.
E/flutter (11326): #0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:4347:9)
E/flutter (11326): #1      Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:4361:6)
E/flutter (11326): #2      Element.getElementForInheritedWidgetOfExactType (package:flutter/src/widgets/framework.dart:4396:12)
E/flutter (11326): #3      ProviderScope.containerOf (package:flutter_riverpod/src/framework.dart:102:12)
E/flutter (11326): #4      ConsumerStatefulElement.read (package:flutter_riverpod/src/consumer.dart:613:26)
E/flutter (11326): #5      _DataScreenState.build.<anonymous closure> (package:flt_playground/screens/screen_data.dart:481:41)
E/flutter (11326): <asynchronous suspension>

To prevent this I also check if widget is mounted in the if statement. But the service takes my http request and I definitely need to update the state by calling that ref.read method.

How I can best handle this scenario? Should I prevent user closing the page while request is loading? What is the best practice for this use case?

Thank you for your answers and time..


Solution

  • As the error states, the problem is calling ref.read after an asynchronous computation. Since the user can pop the page, ref.read will cause the error.

    If all you're trying to do is make sure the provider's method is executed, then consider storing a reference before any asynchronous work takes place.

    Future<void> _computation() async {
      /// Obtain a reference to your provider
      final provider = ref.read(dataProvider.notifier);
    
      /// Mock HTTP request.
      await Future.delayed(const Duration(seconds: 3));
    
      /// Mock data.
      final jsonBody = {
        "status": "OK",
        "data": 1,
      };
      
      /// Note that you no longer have to check [mounted].
      if ((jsonBody["status"] as String).compareTo("OK") == 0) {
        provider.state.setData(jsonBody["data"] as int);
      }
    }
    

    After 3 seconds, the setData method of your provider will be called.