1

I have a PermissionsManager class, and I'm getting a "Do not use BuildContext across async gaps" for this particular method:

class PermissionsManager {
  static Future<void> requestLocationPermission(BuildContext context) async {
    final status = await Permission.location.request();

    if (!status.isGranted) {
      await showOpenSettingsDialog(context,
          title: "Grant Location Access",
          message:
              "TODO");
    }
  }
}

I thought about splitting this into multiple functions, but then the caller needs to check the status, and based on the status call another method that will show this dialog box.

Is there a way to do this in the same method and handle this build context issue?

user1354934
  • 8,139
  • 15
  • 50
  • 80

2 Answers2

1

Good question! Assuming you are in a "Stateful Widget", add if (mounted) check before using BuildContext across an async gap.

For example:

    onPressed: () async {
      final status = await Future.delayed(const Duration(seconds: 1));
      if (mounted) { // <-- add this check
        if (!status.isGranted) {
          showOpenSettingsDialog(context);
        }
      }
    }

The reason we don't want to use BuildContext across an async gap is because the widget could've become unmounted during the wait. If we check if (mounted) we won't have this concern. Basically, if the widget is somehow destroyed during the wait, we just don't show the dialog any more.

If this is a stateless widget, you can convert it into a stateful one.

You can also read my detailed explanation on this topic here.

WSBT
  • 33,033
  • 18
  • 128
  • 133
  • I see. Since this isn't a widget, but rather just a simple class with some static methods, how can I know if it is mounted? I guess that's my confusion because this lives outside of the UI widgets, and in its own thing. Thanks! – user1354934 Jul 15 '22 at 16:29
0

Store the NavigatorState before executing your requestLocationPermission function, and then use it to handle the navigation:

onPressed: () async {
  final navigator = Navigator.of(context); // store the Navigator
  await requestLocationPermission(navigator); // use the Navigator, not the BuildContext
},
class PermissionsManager {
  static Future<void> requestLocationPermission(NavigatorState navigator) async {
    final status = await Permission.location.request();

    if (!status.isGranted) {
      await showOpenSettingsDialog(
          context,
          title: "Grant Location Access",
          message: "TODO",
      );
    }
    navigator.pop(); // Do whatever with your navigator;
  }
}

This answer is basically a shorthand of: https://stackoverflow.com/a/69512692

Which I highly suggest for you to look at it for a more detailed explanation.

Lucas Macêdo
  • 23
  • 1
  • 7