【问题标题】:Error thrown on navigator pop until : "!_debugLocked': is not true."导航器弹出错误直到:“!_debugLocked':不正确。”
【发布时间】:2019-09-01 06:37:45
【问题描述】:

当通过单击 showBottomSheet 弹出一个导航到另一个屏幕时,通过以下代码会引发此错误。我不明白为什么会这样。

class _CheckoutButtonState extends State<_CheckoutButton> {
  final GlobalKey<ScaffoldState> _globalKey = GlobalKey();
  final DateTime deliveryTime = DateTime.now().add(Duration(minutes: 30));

  final double deliveryPrice = 5.00;

  @override
  Widget build(BuildContext context) {
    SubscriptionService subscriptionService =
        Provider.of<SubscriptionService>(context);
    CheckoutService checkoutService = Provider.of<CheckoutService>(context);
    return Container(
      height: 48.0,
      width: MediaQuery.of(context).size.width * 0.75,
      child: StreamBuilder(
        stream: subscriptionService.subscription$,
        builder: (_, AsyncSnapshot<Subscription> snapshot) {
          if (!snapshot.hasData) {
            return Text("CHECKOUT");
          }
          final Subscription subscription = snapshot.data;
          final List<Order> orders = subscription.orders;
          final Package package = subscription.package;
          num discount = _getDiscount(package);
          num price = _totalPriceOf(orders, discount);
          return StreamBuilder<bool>(
              stream: checkoutService.loading$,
              initialData: false,
              builder: (context, snapshot) {
                bool loading = snapshot.data;
                return ExtendedFloatingActionButton(
                  loading: loading,
                  disabled: loading,
                  action: () async {
                    checkoutService.setLoadingStatus(true);
                    final subscription =
                        await Provider.of<SubscriptionService>(context)
                            .subscription$
                            .first;
                    try {
                      await CloudFunctions.instance.call(
                          functionName: 'createSubscription',
                          parameters: subscription.toJSON);
                      final bottomSheet =
                          _globalKey.currentState.showBottomSheet(
                        (context) {
                          return Container(
                            width: MediaQuery.of(context).size.width,
                            decoration: BoxDecoration(
                              gradient: LinearGradient(
                                begin: Alignment.topCenter,
                                end: Alignment.bottomCenter,
                                colors: [
                                  Theme.of(context).scaffoldBackgroundColor,
                                  Theme.of(context).primaryColor,
                                  Theme.of(context).primaryColor,
                                ],
                                stops: [-1.0, 0.5, 1.0],
                              ),
                            ),
                            child: Column(
                              children: <Widget>[
                                Expanded(
                                  child: Column(
                                    mainAxisAlignment: MainAxisAlignment.center,
                                    children: <Widget>[
                                      Padding(
                                        padding:
                                            const EdgeInsets.only(bottom: 16.0),
                                        child: Text(
                                          "Thank you for your order",
                                          textAlign: TextAlign.center,
                                          style: Theme.of(context)
                                              .textTheme
                                              .display1,
                                        ),
                                      ),
                                      SvgPicture.asset(
                                        'assets/images/thumb.svg',
                                        height: 120.0,
                                        width: 100.0,
                                      )
                                      // CircleAvatar(
                                      // radius: 40.0,
                                      // backgroundColor: Colors.transparent,
                                      // child: Icon(
                                      // Icons.check,
                                      // color: Theme.of(context)
                                      // .textTheme
                                      // .display1
                                      // .color,
                                      // size: 80.0,
                                      // ),
                                      // ),
                                    ],
                                  ),
                                ),
                                Container(
                                  width:
                                      MediaQuery.of(context).size.width * 0.9,
                                  height: 72.0,
                                  padding: EdgeInsets.only(bottom: 24),
                                  child: ExtendedFloatingActionButton(
                                    text: "ORDER DETAILS",
                                    action: () {
                                      Navigator.of(context).pop();
                                    },
                                  ),
                                ),
                              ],
                            ),
                          );
                        },
                      );
                      bottomSheet.closed.then((v) {
                        Navigator.of(context)
                            .popUntil((r) => r.settings.isInitialRoute);
                      });
                    } catch (e) {
                      print(e);
                      final snackBar =
                          SnackBar(content: Text('Something went wrong!'));
                      Scaffold.of(context).showSnackBar(snackBar);
                    }
                  },
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Text(
                        "CHECKOUT ",
                        style: Theme.of(context)
                            .textTheme
                            .display4
                            .copyWith(color: Colors.white),
                      ),
                      Text(
                        "EGP " +
                            (price + (orders.length * deliveryPrice))
                                .toStringAsFixed(2),
                        style: Theme.of(context)
                            .textTheme
                            .display4
                            .copyWith(color: Theme.of(context).primaryColor),
                      ),
                    ],
                  ),
                );
              });
        },
      ),
    );
  }

  num _totalPriceOf(List<Order> orders, num discount) {
    num price = 0;
    orders.forEach((Order order) {
      List<Product> products = order.products;
      products.forEach((Product product) {
        price = price + product.price;
      });
    });
    num priceAfterDiscount = price * (1 - (discount / 100));
    return priceAfterDiscount;
  }

  num _getDiscount(Package package) {
    if (package == null) {
      return 0;
    } else {
      return package.discount;
    }
  }
}

错误:

>══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (24830): The following assertion was thrown building Navigator-[GlobalObjectKey<NavigatorState>
I/flutter (24830): _WidgetsAppState#90d1f](dirty, state: NavigatorState#6b2b6(tickers: tracking 1 ticker)):
I/flutter (24830): 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 1995 pos 12: '!_debugLocked':
I/flutter (24830): is not true.
I/flutter (24830): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (24830): more information in this error message to help you determine and fix the underlying cause.
I/flutter (24830): In either case, please report this assertion by filing a bug on GitHub:
I/flutter (24830):   https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter (24830): When the exception was thrown, this was the stack:

【问题讨论】:

    标签: dart flutter navigator


    【解决方案1】:

    我收到此错误是因为我的 initialRoute/login。但是,initialRoute 必须是 /

    如果路由名称以斜线开头,则将其视为“深层链接”,并且在推送此路由之前,还会推送指向该路由的路由。例如,如果路由是 /a/b/c,那么应用程序会以四个路由 /、/a、/a/b 和 /a/b/c 的顺序开始加载。

    这是一个link 供参考的文档。

    【讨论】:

      【解决方案2】:

      我在重新运行应用程序时收到此错误。第一次运行我的代码有效。所以我明白导航器必须等待片刻,直到所有控制器都加载完毕。所以最好的方法是在调用导航器之前使用 Future.delayed。

      Future.delayed(Duration.zero, () {
      // using GetX navigator here you can also use normal navigator
          Get.offNamedUntil(Routes.HOME, (route) => false);
      });
      

      【讨论】:

        【解决方案3】:

        对于在使用 bloc 时遇到此问题的人,请确保您在 BlocListener(或 BlocConsumer 的侦听器)中使用导航。就我而言,我在 BlocBuilder 中使用 Navigator。我是 Flutter/Bloc 的新手,接受的答案解决了问题,但不是正确的解决方案。将我的 BlocBuilder 切换为 BlocConsumer 允许我在特定状态下导航。

        使用 BlocConsumer 的示例,当状态为 'LoginSuccess' 时导航:

        BlocConsumer<LoginBloc, LoginState>(
            listener: (BuildContext context, state) {
              if (state is LoginSuccess) {
                Navigator.of(context).pushReplacement(
                  // Add your route here
                  PageRouteBuilder(
                    pageBuilder: (_, __, ___) => BlocProvider.value(
                      value: BlocProvider.of<NavigationBloc>(context),
                      child: HomeScreen(),
                    ),
                  ),
                );
              }
            },
            // Only build when the state is not LoginSuccess
            buildWhen: (previousState, state) {
              return state is! LoginSuccess;
            },
            // Handle all states other than LoginSuccess here
            builder: (BuildContext context, LoginState state) {
              if (state is LoginLoading) {
                return Center(child: CircularProgressIndicator());
              } else .....
        

        【讨论】:

        • 嗨,山姆,非常感谢。真的很管用!
        【解决方案4】:

        我正在使用颤振版本2.3.3 当我尝试使用命令Navigator.pop(context) 从第二个屏幕弹回我的主屏幕时,我也遇到了这个问题 我通过用Navigator.of(context).pop(context) 替换这行代码解决了这个问题 它对我来说很好,希望它能帮助

        【讨论】:

          【解决方案5】:

          添加一些延迟然后尝试这样做您的问题将得到解决:

            Future.delayed(const Duration(milliseconds: 500), () {
          

          // 在这里你可以写你的代码

                  setState(() {
                    Navigator.of(context).pushAndRemoveUntil(
                        MaterialPageRoute(builder: (context) => SetCategory()),
                        (route) => false);
                  });
                });
          

          【讨论】:

            【解决方案6】:

            对于那些在构建过程中调用Navigator 的人。我发现它会在debugLocked 上间歇性地抛出断言错误

            我通过使用addPostFrameCallback 包装避免了这个问题:

            WidgetsBinding.instance.addPostFrameCallback((_) {
              Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => MyPage()));
            });
            

            【讨论】:

            • 我可以保证,作为 Flutter 的初学者,我不知道该放在哪里。
            • 使用 Navigator.pushReplacementNamed 实现后,它不会导航到下一条路线。现在甚至没有显示以前的错误。但是,当我按下该按钮以重复导航过程时,我会收到此问题中提到的错误。请帮忙。自从 2 天以来,我就一直被这件事困住了。
            • @CristianG 如果您在build 进程中调用导航器,只需将您的Navigator 语句与addPostFrameCallback 包装起来。否则你不必
            • @ChetanGoyal 如果它不是构建过程的一部分,则不需要使用此解决方案。你在按下按钮时调用它不是吗?
            • @sk 为了更好的理解,让我给出一个详细的答案。我已将 onPressed 设为异步,并且正在使用 await 从 firestore 获取一些数据。加载数据后,根据接收到的数据的条件,我将使用 await 从 firestore 加载更多数据。然后在检查最终收到的数据后,我正在调用导航器。我已经尝试过延迟和回调方法,它们没有奏效。我尝试在最终数据检查后创建一个新的布尔值并使用 setState,并在基于该布尔值的构建之上使用 Navigator。但所有这些变通方法都没有奏效。 :(
            【解决方案7】:

            对于那些仍然有同样问题的人,这有助于我解决它。

            navigationService.popUntil((_) => true);
            navigationService.navigateTo(
              'authentication',
            );
            

            基本上我等到导航完成设置所有内容然后调用navigateTo。

            【讨论】:

              【解决方案8】:

              在简历中,您只需将其从您的 initState 中删除。 我建议使用 AfterLayout 和内部扩展类 afterFirstLayout 你可以将它重定向到你想要的页面。这将保证在路由之前一切正常。

              请参阅以下步骤: 添加到 pubspec: after_layout: ^1.0.7+2

              然后,您将其扩展到您要使用的类。在我的例子中是一个名为 HomePage 的有状态小部件。所以它看起来像:

              class HomePage extends StatefulWidget {
                @override
                HomePageState createState() => HomePageState();
              } //no changes here
              
              
              class HomePageState extends State<HomePage> with AfterLayoutMixin<HomePage> {
              //the with AfterLayoutMixin<pageName> is the only thing you need to change.
              

              现在,您需要实现一个名为 afterlayout 的方法,该方法将在构建完成后执行。

              @override
                Future<void> afterFirstLayout(BuildContext context) {
              
                //your code here safetly
              }
              

              您可以在此处找到信息: https://pub.dev/packages/after_layout

              【讨论】:

                【解决方案9】:

                对我来说,它即将到来是因为我创建了一个导致此错误的推送循环。 例如,

                /loading 的初始路由中,代码正在推送 /home

                class _LoadingState extends State<Loading> {
                  void getTime() async {
                    // DO SOME STUFF HERE
                    Navigator.pushNamed(context, '/home');
                  }
                
                  @override
                  void initState() {
                    super.initState();
                    getTime();
                  }
                
                

                /home initState 中,我正在推动/loading 创建一个循环。

                class _HomeState extends State<Home> {
                  @override
                  void initState() {
                    super.initState();
                    Navigator.pushNamed(context, '/loading');
                  }
                

                【讨论】:

                  【解决方案10】:

                  我遇到了同样的问题,任何答案都不适合我,而且这个错误不能解释任何事情。

                  在完成每一行代码后,我发现我们无法在这样的构建方法中启动任何state

                   @override
                   Widget build(BuildContext context) {
                       var viewmodel = Provider.of<ViewModel>(context);
                       Navigator.of(context).push(MaterialPageRoute(builder: 
                          (context)=>CreateItemPage(viewmodel.catalogData))); // this is way i was getting error.
                  
                  return Scaffold();
                  }
                  

                  因为那条线,我在CreateItemPage 屏幕上遇到了错误。

                  此问题的解决方案创建button 调用此行Navigator.of(context).push(MaterialPageRoute(builder: (context)=&gt;CreateItemPage(viewmodel.catalogData)));

                  【讨论】:

                  • 我想在某些情况下自动导航。所以在我的情况下,按钮不是 bueno。
                  • @CsabaToth 有两种方法。第一次使用一个通用容器屏幕并将您的状态作为小部件,第二次使用ChangeNotifier
                  【解决方案11】:

                  我遇到了类似的错误,比如一个对话框有一个注销按钮,当按下它时会进入登录屏幕,但出现_debugLocked 错误,所以我使用了

                  Navigator.of(context).pushNamedAndRemoveUntil('/screen4', (Route<dynamic> route) => false);
                  

                  这会删除堆栈中的所有路由,以便用户在退出后无法返回之前的路由。

                  设置(Route&lt;dynamic&gt; route) =&gt; false会确保推送路由之前的所有路由都被移除。

                  我不知道这是否是“真正的”解决方案,但它帮助了我作为 Flutter 的初学者。

                  【讨论】:

                  • 这对我来说很快,谢谢。我无法弄清楚导航器控制屏幕的问题。不过后面我会做一个简单的项目来更好的分析和理解Navigator。
                  • 使用这个后我仍然遇到同样的问题
                  【解决方案12】:

                  我不会直接给你答案,而是告诉你我看到这个问题时的想法,希望它对你将来有所帮助。

                  让我们看一下断言。它说Failed assertion: line 1995 pos 12: '!_debugLocked': I/flutter (24830): is not true.。嗯,有趣。我们来看看那行代码。

                  assert(!_debugLocked);

                  好吧,这并没有给我更多的信息,让我们看看变量。

                  bool _debugLocked = false; // used to prevent re-entrant calls to push, pop, and friends

                  这样更好。它的存在是为了防止对 push、pop 等的重入调用(这意味着它不希望您在对“push”、“pop”的调用中调用“push”、“pop”等)。因此,让我们将其追溯到您的代码。

                  这似乎是罪魁祸首:

                  bottomSheet.closed.then((v) {
                    Navigator.of(context)
                      .popUntil((r) => r.settings.isInitialRoute);
                  });
                  

                  我将在这里跳过一个步骤并改用演绎推理 - 我打赌封闭的未来在 pop 期间完成。如果您愿意,可以通过阅读代码来确认这一点。

                  因此,如果问题是我们从 pop 函数中调用 pop,我们需要找到一种方法来推迟对 pop 的调用,直到 pop 完成之后。

                  这变得非常简单 - 有两种方法可以做到这一点。简单的方法是只使用零延迟的延迟未来,一旦当前调用堆栈返回事件循环,dart 会尽快安排调用:

                  Future.delayed(Duration.zero, () {
                    Navigator. ...
                  });
                  

                  另一种更流畅的方式是使用调度器在当前构建/渲染周期完成后安排调用:

                  SchedulerBinding.instance.addPostFrameCallback((_) {
                    Navigator. ...
                  });
                  

                  无论哪种方式都应该可以消除您遇到的问题。

                  另一个选项也是可能的 - 在您调用 pop 的 ExtendedFloatingActionButton 中:

                  ExtendedFloatingActionButton(
                   text: "ORDER DETAILS",
                    action: () {
                      Navigator.of(context).pop();
                    },
                  ),
                  

                  您可以改为简单地调用Navigator.of(context).popUntil...。这将消除调用 bottomSheet.close 后做任何事情的需要。但是,根据您在逻辑中可能需要或不需要执行的其他任何操作,这可能并不理想(我绝对可以看到让底部工作表对页面主要部分进行更改的问题以及您为什么试图在页面的逻辑中实现这一点)。

                  另外,当您编写代码时,我强烈建议将其分成小部件 - 例如,底部工作表应该是它自己的小部件。您在构建功能中拥有的越多,它就越难遵循,它实际上也会对性能产生影响。您还应该尽可能避免使用 GlobalKey 实例 - 如果对象(或回调)仅通过几层,您通常可以向下传递对象(或回调),使用 .of(context) 模式,或使用继承的小部件。

                  【讨论】:

                  • 是的,非常好的答案,更多的是关于如何解决问题而不仅仅是解决方案。它确实帮助我找到了需要放入 SchedulerBinding() 的区域。谢谢!
                  • 令人印象深刻!非常详细和一致的方式来推动学习,而不是简单地给你糖果:) 谢谢@rmtmckenzie
                  • 唯一对我有用的是使用SchedulerBinding
                  • 有点跑题了,但它可能对某人有所帮助,我的问题是我忘记在我用完全不同的密钥重建的小部件上的dispose() 方法中添加WidgetsBinding.instance.removeObserver(this); Constants.routeObserver.unsubscribe(this);,不知何故这被阻止了导航器类,当我在使用setState 重建后尝试导航时收到此消息@
                  • 干得好!一个很好的解释导致解决方案,而不仅仅是像其他答案那样的解决方案。我喜欢你的演绎推理。
                  猜你喜欢
                  • 1970-01-01
                  • 2020-10-30
                  • 2021-08-26
                  • 2022-01-07
                  • 2015-09-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多