在InitState中显示对话框未处理的异常:查找停用的窗口小部件的祖先是不安全的

时间:2020-05-30 18:16:23

标签: flutter

在第一次出现屏幕时,我要检查是否启用了用户GPS并授予了“位置许可”。然后,如果其中之一没有满足,我会显示对话框以打开应用程序设置。

源代码

_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);
        },
      );
    }
  }

然后我像这样在initState中调用此函数:

InitState

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

问题是,每当屏幕第一次出现时,都会给我这样的错误:

错误

[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.

我做了什么:

  • 像这样更改InitState
//1
 WidgetsBinding.instance.addPostFrameCallback((_) {
      _initPermission(context);
 });
//2
  SchedulerBinding.instance.addPostFrameCallback((_) => _initPermission(context));
//3
Timer.run(() { 
      _initPermission(context); 
  })
  • 为脚手架添加全局密钥
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
 Scaffold(
        key: _scaffoldKey,

我完成的结果什么都没有,错误仍然出现在第一次屏幕上。

但是奇怪的是,这仅在第一次出现屏幕时发生。当我热重启时,错误消息消失了。

[如果首次出现屏幕失败]

enter image description here

[热重启,错误消失]

enter image description here

1 个答案:

答案 0 :(得分:0)

我无法对其进行测试(我没有GPS软件包),但尝试更改

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

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

该错误在热重启中消失了,因为它只是刷新了小部件的状态,但是它已经创建了(如果您进行热重启并在initState内进行打印,例如print('This is init');,则不会之所以看到它,是因为刷新不会处理并初始化小部件,因此不会再次运行该代码段

编辑

基于您的要点,我只是制作了一个可重现的示例,并且在DartPad中运行没有问题,稍后我将在VS上尝试它,但是现在您可以检查是否有其他区别

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');
  }
}