如何在异步函数中防止Navigator.pop()弹出不再存在的路由了?

发布于 2025-02-07 12:13:51 字数 654 浏览 1 评论 0 原文

在我的Flutter应用程序中,我有一个按钮,该按钮启动了异步网络操作,并在等待它完成时显示对话框。完成后,对话框将从导航中弹出,但是我遇到了并发问题。这是我的代码:

ElevatedButton(
  onPressed: () async {
    showDialog(
      context: context,
      builder: (context) => Center(child: CircularProgressIndicator()),
    );
    await asyncNetworkOperation();
    Navigator.pop(context);
  },
  child: Text("Press here");
)

如果在进行网络操作时,用户点击了Android返回按钮,则对话框会提前弹出。然后,一旦网络操作完成,就会发出另一个Navigator.pop(上下文),从而将导航推回一个额外的步骤。

如果用户已经弹出对话框,则避免执行导航器的最佳方法是什么?我知道我可以防止返回按钮与 willPopsCope 窗口小部件完全工作,但是我希望用户能够在操作中花费太长时间能够中止该操作。

TLDR:如何在异步效果中预防 navigator.pop(),以弹出已经弹出的路由?

In my flutter application I have a button which starts an asynchronous network operation and displays a dialog while waiting for it to complete. Once completed, the dialog is popped from the navigation, but I am running into concurrency issues. Here's my code:

ElevatedButton(
  onPressed: () async {
    showDialog(
      context: context,
      builder: (context) => Center(child: CircularProgressIndicator()),
    );
    await asyncNetworkOperation();
    Navigator.pop(context);
  },
  child: Text("Press here");
)

If the user taps on the Android back button while the network operation is in progress, the dialog gets popped ahead of time. And then, once the network operation completes, another Navigator.pop(context) is issued which pushes the navigation back one extra step.

What is the best way to avoid Navigator.pop from executing if the user already popped the dialog? I know I could prevent the back button from working altogether with the WillPopScope widget, but I would like the user to have the ability to abort the operation should it take too long.

TLDR: How to prevent a Navigator.pop() in an async frunction from popping a route which has already been popped?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

甲如呢乙后呢 2025-02-14 12:13:51

一种多功能方法(可能是建议)正在使用已安装的属性,该属性需要您使用 statefulwidget

class MyDialog extends StatefulWidget {
  const MyDialog({Key? key}) : super(key: key);

  @override
  State<MyDialog> createState() => _MyDialogState();
}

class _MyDialogState extends State<MyDialog> {
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('AlertDialog Title'),
      content: const Text('AlertDialog description'),
      actions: <Widget>[
        TextButton(
          onPressed: () async {
            await Future.delayed(const Duration(seconds: 5));
            if (!mounted) {
              return;
            }
            Navigator.pop(context, 'Cancel');
          },
          child: const Text('Cancel'),
        ),
      ],
    );
  }
}

创建 state> state object和nated code> initstate ,框架通过将其与 buildContext 关联来“安装”状态对象。 状态对象保持安装直到框架调用处置,此后,框架将永远不会要求 state> state 对象再次构建,因此您可以在安全使用上下文对象之前,请检查已安装的属性,除了弹出导航器外,其他目的。


See:

  1. https://dart-lang.github.io/linter/lints/use_build_context_synchronously. html
  2. https://api.flutter.dev/flutter.dev/flutter.dev/flutter.dev/flutter/widgetss /state/mounted.html

A versatile approach (and probably recommended) is using the mounted property, which would require you to use a StatefulWidget:

class MyDialog extends StatefulWidget {
  const MyDialog({Key? key}) : super(key: key);

  @override
  State<MyDialog> createState() => _MyDialogState();
}

class _MyDialogState extends State<MyDialog> {
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('AlertDialog Title'),
      content: const Text('AlertDialog description'),
      actions: <Widget>[
        TextButton(
          onPressed: () async {
            await Future.delayed(const Duration(seconds: 5));
            if (!mounted) {
              return;
            }
            Navigator.pop(context, 'Cancel');
          },
          child: const Text('Cancel'),
        ),
      ],
    );
  }
}

After creating a State object and before calling initState, the framework "mounts" the State object by associating it with a BuildContext. The State object remains mounted until the framework calls dispose, after which time the framework will never ask the State object to build again, therefore you can check the mounted property before safely using the context object, for other purposes other than just popping the navigator as well.


See:

  1. https://dart-lang.github.io/linter/lints/use_build_context_synchronously.html
  2. https://api.flutter.dev/flutter/widgets/State/mounted.html
鸵鸟症 2025-02-14 12:13:51

您可以检查对话框是否活动。仅在有主动对话框

示例时返回:

_isOpen = true;


ElevatedButton(
  onPressed: () async {
    showDialog(
      context: context,
      builder: (context) => Center(child: CircularProgressIndicator()),
    ).then((_) => _isOpen = false);
    await asyncNetworkOperation();
    if(_isOpen){
        Navigator.pop(context);
    }
  },
  child: Text("Press here");
)

You could check if the dialog is active or not. Only go back if there is active dialog

Example:

_isOpen = true;


ElevatedButton(
  onPressed: () async {
    showDialog(
      context: context,
      builder: (context) => Center(child: CircularProgressIndicator()),
    ).then((_) => _isOpen = false);
    await asyncNetworkOperation();
    if(_isOpen){
        Navigator.pop(context);
    }
  },
  child: Text("Press here");
)

战皆罪 2025-02-14 12:13:51

可以使用 statefulwidget 来解决此问题。我们可以使用已安装 statefulwidget的属性来检查小部件是否处置。无状态宽松没有安装的属性。

ElevatedButton(
 onPressed: () async {
  showDialog(
    context: context,
    builder: (context) => Center(child: CircularProgressIndicator()),
  );
  await asyncNetworkOperation();
  if(mounted) {
    Navigator.pop(context);
  }
},
 child: Text("Press here");)

This issue can be solved by using the StatefulWidget. We can use mounted property of a StatefulWidget to check wether the widget is disposed or not. There is no mounted property in StatelessWidget.

ElevatedButton(
 onPressed: () async {
  showDialog(
    context: context,
    builder: (context) => Center(child: CircularProgressIndicator()),
  );
  await asyncNetworkOperation();
  if(mounted) {
    Navigator.pop(context);
  }
},
 child: Text("Press here");)
旧梦荧光笔 2025-02-14 12:13:51

不确定,如果这很有意义。

if(ModalRoute.of(dialogContext)?.isCurrent ?? false){
   Navigator.pop(dialogContext);
}

如果我没有错,这是在检查给定上下文的路线当前位于导航器树的顶部。如果是流行音乐,否则不要弹出。在此示例中,我通过的上下文属于对话框。我使用的另一种方式是确保对话框下的小部件不在顶部。

//Assume widgetContext is the Scaffold below my dialog
//and dialogContext is the context of my dialog

if(ModalRoute.of(widgetContext)?.isCurrent != true){
   Navigator.pop(dialogContext);
}

无效的安全性,取决于您,我将“!= true”放置,因为如果它的空,我也不想弹出。无论出于何种原因,WidgetContext的ModalRoute都不会返回我的价值。与为什么我使用“?false”相同

Not sure, if this make sense.

if(ModalRoute.of(dialogContext)?.isCurrent ?? false){
   Navigator.pop(dialogContext);
}

If I am not wrong, this is checking the route of the given context is currently on the top of the navigator tree. If it is pop, else dont pop. In this example the context i pass belongs to the dialog. The other way i used was to make sure that the widget under my dialog is not at the top.

//Assume widgetContext is the Scaffold below my dialog
//and dialogContext is the context of my dialog

if(ModalRoute.of(widgetContext)?.isCurrent != true){
   Navigator.pop(dialogContext);
}

Null safety, is up to you, I put "!= true" because if its null i do not want to pop as well. for whatever reason the ModalRoute for the widgetContext does not return me a value. same as why i use "?? false"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文