查找已停用小部件的祖先是不安全的。Navigator.of(context).pushAndRemoveUntil

Mus*_*riq 12 navigation navigator dart flutter

navigator.pushandremoveuntil 工作正常,但抛出异常:此语句是从扩展 ChangeNotifier(提供程序)的类执行的。

在完成小部件树时抛出以下断言:查找已停用小部件的祖先是不安全的。

此时小部件的元素树的状态不再稳定。

要在 widget 的 dispose() 方法中安全地引用它的祖先,请通过在 widget 的 didChangeDependency() 方法中调用 dependentOnInheritedWidgetOfExactType() 来保存对祖先的引用。

Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (_) {return EmailAuthVC();}), (Route<dynamic> route) => false);
Run Code Online (Sandbox Code Playgroud)

Lul*_*ntu 40

错误是什么?

正如错误所解释的,您正在尝试dependOnInheritedWidgetOfExactType在 dispose 方法中使用。

这真正意味着context您正在使用的不再是小部件树的一部分(因为它的状态已被释放),因此您不能使用它来调用dependOnInheritedWidgetOfExactType.

但你在哪里使用dependOnInheritedWidgetOfExactType?在Navigator.of(context)。如果你检查它的源代码:

static NavigatorState of(
  BuildContext context, {
  bool rootNavigator = false,
}) {
  // Handles the case where the input context is a navigator element.
  NavigatorState? navigator;
  if (context is StatefulElement && context.state is NavigatorState) {
    navigator = context.state as NavigatorState;
  }
  if (rootNavigator) {
    navigator = context.findRootAncestorStateOfType<NavigatorState>() ?? navigator;
  } else {
    navigator = navigator ?? context.findAncestorStateOfType<NavigatorState>();
  }
  ...
  return navigator!;
}
Run Code Online (Sandbox Code Playgroud)

怎么解决这个问题呢?

您必须使用上下文来获取方法Navigator 之前的dispose内容。正如错误中所解释的,您应该创建对该对象的引用(didChangeDependencies例如在方法中)并稍后使用它。

这是一个具体的例子:

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  // The reference to the navigator
  late NavigatorState _navigator;

  @override
  void didChangeDependencies() {
    _navigator = Navigator.of(context);
    super.didChangeDependencies();
  }

  @override
  void dispose() {
    _navigator.pushAndRemoveUntil(..., (route) => ...);
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return ...;
  }
}
Run Code Online (Sandbox Code Playgroud)

为什么不使用该initState方法而不是didChangeDependencies?因为,尽管context中 不再有效dispose,但context中 中 尚未有效initState,因为该小部件尚未插入到小部件树中。


小智 5

就在几个小时前,我也遇到了同样的问题,我得到的错误与您在问题中提到的完全相同。当我试图知道到底出了什么问题时,即使调用一个简单的导航器,我也发现我正在调用一个已经处理的小部件的上下文。确切的情况是,我正在构建一个测验应用程序,当时间结束时,用户将被重定向到结果屏幕。所以我把导航器称为

Future.delayed(Duration(seconds: quiz.quizDuration)).then((value) {
    // go to result screen when time is over
    Navigator.pushReplacement(context,
        MaterialPageRoute(builder: (context) => const ResultScreen()));
});
Run Code Online (Sandbox Code Playgroud)

但如果用户提前回答了所有问题,他也会被重定向到结果屏幕。所以这个小部件已经被释放了,我们的延迟函数将在该时间后完成,并尝试使用已释放小部件的上下文。这就是错误。

我通过检查状态是否持续解决了这个问题。如果小部件仍然存在,则将其处理掉,如果它已经被处理掉,则不执行任何操作。

Future.delayed(Duration(seconds: quiz.quizDuration)).then((value) {
  // check if is mounted
  if (mounted) {
    // if it is mounted then go to result screen, time is off bro..
    Navigator.pushReplacement(context,
        MaterialPageRoute(builder: (context) => const ResultScreen()));
  }
});
Run Code Online (Sandbox Code Playgroud)

这就是我解决问题的方法。所以我想你也可以通过将导航器代码放入其中来解决你的问题

if(mounted) 
{
// navigator.... 
}
Run Code Online (Sandbox Code Playgroud)