0

At first time screen appears, i want check if user GPS is enable and Location Permission is granted. Then if one of them is not fulfilled , i show dialog to open app setting.

Source Code

_initPermission(BuildContext context) async {
    final geolocationStatus = await commonF.getGeolocationPermission();
    final gpsStatus = await commonF.getGPSService();
    if (geolocationStatus != GeolocationStatus.granted) {
      showDialog(
        context: context,
        builder: (ctx) {
          return commonF.showPermissionLocation(ctx);
        },
      );
    } else if (!gpsStatus) {
      showDialog(
        context: context,
        builder: (ctx) {
          return commonF.showPermissionGPS(ctx);
        },
      );
    }
  }

Then i called this function in initState like this :

InitState

void initState() {
    super.initState();
    Future.delayed(Duration(milliseconds: 50)).then((_) => _initPermission(context));
  }

The problem is , every first time the screen appears it will give me error like this :

Error

[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] 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.

What i have done :

  • Change InitState like this
//1
 WidgetsBinding.instance.addPostFrameCallback((_) {
      _initPermission(context);
 });
//2
  SchedulerBinding.instance.addPostFrameCallback((_) => _initPermission(context));
//3
Timer.run(() { 
      _initPermission(context); 
  })
  • Adding Global Key For Scaffold
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
 Scaffold(
        key: _scaffoldKey,

The results that I have done is nothing , the error still appear in first time screen appear.

But strangely, this only happens when first time screen appear . When i do Hot Restart the error message is gone.

[Failed if screen first time appear]

enter image description here

[Hot restart , error gone]

enter image description here

Zeffry Reynando
  • 3,445
  • 12
  • 49
  • 89

1 Answers1

0

I have no way to test it (I dont have the GPS package) but try changing

Future.delayed(Duration(milliseconds: 50)).then((_) => _initPermission(context));

to

Future.delayed(Duration(milliseconds: 50)).then((_) => _initPermission()); //this inside the initstate

_initPermission() async{
final geolocationStatus = await commonF.getGeolocationPermission();
final gpsStatus = await commonF.getGPSService();
if (geolocationStatus != GeolocationStatus.granted) {
  await showDialog(
    context: context,
    builder: (ctx) => commonF.showPermissionLocation
    },
  );
} else if (!gpsStatus) {
  await showDialog(
    context: context,
    builder: (ctx) => commonF.showPermissionGPS
    },
  );
}
} // the stateful widget can use the context in all its methods without passing it as a parameter

The error dissapears in hot restart because it just refresh the state of the widget, but it's already created (If you do hot restart and print something inside the initState, for example print('This is init');, you wont see it either because the refresh doesn't dispose and init the widget, so it won't run that piece of code again)

EDIT

Based on your gist I just made a minimal reproducible example and run with no problem in DartPad, later I will try it on VS but for now can you check if there is anything different

enum GeolocationStatus{granted, denied} //this is just for example

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {


showPermissionGPS() {
    return AlertDialog(
        title: Text('GPSStatus'),
      );
  }

  _showPermissionLocation() {
    return AlertDialog(
        title: Text('Permission Localization'),
      );
  }

  @override
  initState(){
    super.initState();
    Future.delayed(Duration(milliseconds: 50)).then((_) => _initPermission());
  }


_initPermission() async{
  final geolocationStatus = await Future<GeolocationStatus>.value(GeolocationStatus.granted); //mocked future with a response to test
  final gpsStatus = await Future<bool>.value(false); //mocked future with a response to test
  if (geolocationStatus != GeolocationStatus.granted) {
   await showDialog(
      context: context,
      builder: (ctx) => _showPermissionLocation() //this mock commonF.showPermissionLocation and returns an AlertDialog
   );
  } else if (!gpsStatus) {
   await showDialog(
    context: context,
    builder: (ctx) => _showPermissionGPS() //this mock commonF.showPermissionGPS and returns an AlertDialog
  );
 }
}

  @override
  Widget build(BuildContext context) {
    return Text('My text');
  }
}
EdwynZN
  • 4,895
  • 2
  • 12
  • 15
  • Already try this , it will make new error `'context != null': is not true.`. The error will appoint to `initState` and `showDialog` – Zeffry Reynando May 31 '20 at 01:47
  • commonF Is from what package? – EdwynZN May 31 '20 at 02:37
  • `commonF` is all my separated function stored in 1 file. https://gist.github.com/zgramming/2f431fdf1db72c7b9b6774017909a0eb – Zeffry Reynando May 31 '20 at 02:42
  • how about commonF.showPermissionGPS and commonF.showPermissionLocation? are those methods that return a Widget? I edited my answer of the dialogs to return with that ctx – EdwynZN May 31 '20 at 03:05
  • https://gist.github.com/zgramming/2f431fdf1db72c7b9b6774017909a0eb I update the gist make it more clear. `showPermissionGPS` & `showPermissionLocation` it return alertDialog. can be seen here https://gist.github.com/zgramming/95fc8e55da2b9b85cb0fd57a24b840c6 – Zeffry Reynando May 31 '20 at 03:11
  • I edited my answer with a minimal reproducible example, just to test the logic of the widgets – EdwynZN May 31 '20 at 03:50