Home > Mobile >  Why do Navigator.pop(context) must run several times to close a dialog?
Why do Navigator.pop(context) must run several times to close a dialog?

Time:12-13

I try to select a file in file picker and process the situation when a wrong file type was selected. So the code throws a _wrongFileTypeDialog which gives options to cancel the dialog or select file again. The problem is when I choose wrong file type several times then if I want to cancel the dialog I need to press cancel button several times (actualy I need to press it the same number of times the wrong file type was chosen).

If I put Navigator.pop(context); before _getFile(context); then I get another more complicated error Looking up a deactivated widget's ancestor is unsafe. At this point the state of the widget's element tree is no longer stable.

A starting point - a button which runs _getFile(context) to open a file picker.

Future<void> _getFile(BuildContext context) async {
    final FlutterDocumentPickerParams params = FlutterDocumentPickerParams(
      allowedFileExtensions: ['txt'],
    );

    String errorMessage;

    final String path =
        await FlutterDocumentPicker.openDocument(params: params).catchError((dynamic e) {
      errorMessage = e.toString();
      debugPrint(errorMessage);
    });

    if (path == null) {
      if (errorMessage != null && errorMessage.contains('Picked file extension mismatch!')) {
        _wrongFileTypeDialog(context);
        return;
      }
      
      return;
    }
}

void _wrongFileTypeDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        title: const Text('Wrong file type'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              // Navigator.pop(context);
              _getFile(context);
            },
            child: const Text('Choose file'),
          ),
        ],
      ),
    );
}

Obviously I could run this to close the dialog

while (Navigator.canPop(context)) {
    Navigator.pop(context);
}

but it's more like a temporary workaround.

CodePudding user response:

I try to select a file in file picker and process the situation when a wrong file type was selected. So the code throws a _wrongFileTypeDialog which gives options to cancel the dialog or select file again. The problem is when I choose wrong file type several times then if I want to cancel the dialog I need to press cancel button several times (actualy I need to press it the same number of times the wrong file type was chosen).

You're calling the _wrongFileTypeDialog recursively each time you choose wrong file type:

Future<void> _getFile(BuildContext context) async {
    ...

    if (path == null) {
      if (errorMessage != null && errorMessage.contains('Picked file extension mismatch!')) {
        _wrongFileTypeDialog(context);
        return;
      }
      
      return;
    }
}

and _getFile will be called each time you press the button:

void _wrongFileTypeDialog(BuildContext context) {
    ...
    onPressed: () {
      _getFile(context);
    },
  ...

}

So, it will keep calling themeself.

If I put Navigator.pop(context); before _getFile(context); then I get another more complicated error Looking up a deactivated widget's ancestor is unsafe. At this point the state of the widget's element tree is no longer stable.

When you're calling the Navigator.pop(context) in the following code:

void _wrongFileTypeDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        ...
        actions: [
          ...
            onPressed: () {
              Navigator.pop(context);
              _getFile(context);
            },
          ...
          ),
        ],
      ),
    );
}

You're popping the AlertDialog with its context. So, when _getFile(context); is executed, the context is already gone.

You need to use context from the parent of the method. So, change the parameter context name so that it not overshadowed by AlertDialog context:

void _wrongFileTypeDialog(BuildContext ctx) {
    showDialog(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        ...
        actions: [
          ...
            onPressed: () {
              Navigator.pop(context);
              _getFile(ctx);
            },
          ...
          ),
        ],
      ),
    );
}
  • Related