当通过单击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:
我不会给你一个直接的答案,而是引导你了解我在看到这个问题时是如何思考这个问题的,希望它能在未来对你有所帮助。
让我们来看看这个断言。它说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"等)。因此,让我们将其追溯到您的代码。
这似乎是可能的罪魁祸首:
bottomSheet.closed.then((v) {
Navigator.of(context)
.popUntil((r) => r.settings.isInitialRoute);
});
我将跳过这里的一步,改用演绎推理 - 我打赌封闭的未来在pop
内完成.如果您愿意,请继续通过阅读代码来确认这一点。
因此,如果问题是我们从 pop 函数中调用 pop,我们需要找到一种方法来将 pop 的调用推迟到弹出函数完成后。
这变得非常简单 - 有两种方法可以做到这一点。简单的方法是只使用零延迟的延迟未来,一旦当前调用堆栈返回到事件循环,dart 就会尽快安排调用:
Future.delayed(Duration.zero, () {
Navigator. ...
});
另一种更轻飘飘的方法是在当前构建/渲染周期完成后使用调度程序来调度调用:
SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator. ...
});
无论哪种方式都应该消除您遇到的问题。
不过,另一种选择也是可能的 - 在您的扩展浮动动作按钮中,您调用 pop:
ExtendedFloatingActionButton(
text: "ORDER DETAILS",
action: () {
Navigator.of(context).pop();
},
),
相反,您可以简单地调用Navigator.of(context).popUntil...
。这将消除在调用 bottomSheet.closed 后执行任何操作的需要。但是,根据您在逻辑中可能需要或不需要执行的其他操作,这可能并不理想(我绝对可以看到让底部工作表对页面主要部分进行更改的问题,以及为什么您试图在页面的逻辑中发生这种情况)。
此外,当你编写代码时,我强烈建议你把它分成小部件——例如,底部工作表应该是它自己的小部件。构建函数越多,就越难遵循,它实际上也会对性能产生影响。您还应该尽可能避免使用 GlobalKey 实例 - 如果对象(或回调)仅通过几层,您通常可以向下传递对象(或回调),使用 .of(context) 模式或使用继承的小部件。
适用于在构建过程中调用Navigator
的用户。我发现它会间歇性地在debugLocked
上抛出断言错误
我通过用addPostFrameCallback
包装来避免这个问题:
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => MyPage()));
});
添加一些延迟,然后尝试这样做 您的问题将得到解决:
Future.delayed(const Duration(milliseconds: 500), () {
在这里你可以写你的代码
setState(() {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => SetCategory()),
(route) => false);
});
});
我遇到了同样的问题,任何答案都对我不起作用,这个错误并不能解释任何事情。
在执行每一行代码后,我发现我们无法像这样在构建方法中启动任何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)=>CreateItemPage(viewmodel.catalogData)));
对我来说,它的到来是因为我创建了一个导致此错误的推送循环。 例如
在/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');
}
我有类似的错误,比如一个对话框有一个注销按钮,按下时会进入登录屏幕,但发生_debugLocked
错误,所以我使用
Navigator.of(context).pushNamedAndRemoveUntil('/screen4', (Route<dynamic> route) => false);
这将删除堆栈中的所有路由,以便用户在注销后无法返回到以前的路由。
设置(Route<dynamic> route) => false
将确保删除推送路由之前的所有路由。
我不知道这是否是"真正的"解决方案,但它帮助了我作为 Flutter 的初学者。
由于在build()
期间意外调用Navigator.of(context).push
的拼写错误,我得到了此错误:E/flutter ( 6954): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 2845 pos 18: '!navigator._debugLocked': is not true.
模拟器闪烁了一个信息量更大的错误:
setState() or markNeedsBuild() called during build.
This Overlay widget cannot be marked as needing to build because the framework
is already in the process of building widgets. A widget can be marked as needing
to be built during the build phase only if one of its ancestors is currently
building. This exception is allowed because the framework builds parent widgets
before children, which means a dirty descendant will always be built.
Otherwise, the framework might not visit this widget during this build phase The
widget on which setState() or markNeedsBuild() was called was:
Overlay-[LabeledGlobalKey<OverlayState>#de69b]
The widget which was currently being built when the offending call was made was:
FutureBuilder
基本上,您不应该尝试在build
中间推送/弹出到新路线。如果您确实需要,请等待构建完成,这就是为什么其他人建议将其包装在SchedulerBinding.instance.addPostFrameCallback
中以在渲染完所有内容后执行,但您可能应该找到一种更好的方法来在build
之外执行此操作。
就我而言,我输入了:onTap: _onTap(context),
当我真正想输入时:onTap: () => _onTap(context),
我的_onTap
处理程序正在执行导航器push
。我忘记将我的处理程序包装在捕获它所需context
的闭包中,而是实际上正在执行它而不是传递onTap:
我的回调。
对话框解决方案
对于那些从Dialog
打电话给Navigator.push(..)
时遇到这种情况的人。
您需要先以编程方式关闭模式,然后调用Navigator.push(..)
Navigator.pop(context);
。
对于在使用 bloc 时遇到此问题的用户,请确保您在 BlocListener(或 BlocConsumer 的侦听器)中使用导航。就我而言,我在BlocBuilder中使用了Navigator。我是 Flutter/Bloc 的新手,接受的答案解决了问题,但不是正确的解决方案。将我的 BlocBuilder 切换到 BlocConsumer 允许我在特定状态下导航。
使用BlocConsumer的示例,当状态为"登录成功"时导航:
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 .....
在恢复中,你只需要从你的 initState 中删除它。 我建议使用 AfterLayout 和内部扩展类 在FirstLayout之后,您可以将其重定向到所需的页面。这将保证在路由之前一切正常。
请参阅下面的步骤: 添加到公共规格: after_layout: ^1.0.7+2
然后,您将它扩展到要使用的类。在我的情况下,是一个名为主页的有状态小部件。所以它看起来像:
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
对于那些仍然有相同问题的人,这有助于我解决它。
navigationService.popUntil((_) => true);
navigationService.navigateTo(
'authentication',
);
基本上,我等到导航完成所有设置,然后调用navigateTo。
我收到此错误是因为我的initialRoute
/login
。但是,initialRoute
必须/
。
如果路由名称以斜杠开头,则将其视为"深层链接",并且在推送此路由之前,也会推送指向此路由的路由。例如,如果路由是/a/b/c,则应用将按该顺序加载四个路由/、/a、/a/b 和/a/b/c。
这是文档的链接以供参考。
我遇到了同样的问题,花了我一些时间来弄清楚。我正在收听屏幕上的状态,根据该状态它将导航到不同的屏幕。然后在单击按钮时,我正在更改该状态并导航到导致问题的其他屏幕。
我正在使用颤振版本2.3.3
当我尝试使用命令从第二个屏幕弹出主屏幕时,我也遇到了这个问题Navigator.pop(context)
我通过将这行代码替换为Navigator.of(context).pop(context)
解决了这个问题 它对我来说工作正常,希望它很好