Search code examples
flutterdartnavigationnavigator

Looking up a deactivated widget's ancestor is unsafe. Navigator.of(context).pushAndRemoveUntil


navigator.pushandremoveuntil is working fine but an exception is thrown: This statement is executed from a class that extends ChangeNotifier (provider).

The following assertion was thrown while finalizing the widget tree: Looking up a deactivated widget's ancestor is unsafe.

At this point the state of the widget's element tree is no longer stable.

To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.

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

Solution

  • What is the error?

    As the error explains, you are trying to use dependOnInheritedWidgetOfExactType in the dispose method.

    What this really means is that the context you are using is no longer part of the widget tree (because its state as been disposed) and therefore you cannot use it to call dependOnInheritedWidgetOfExactType.

    But where are you using dependOnInheritedWidgetOfExactType? In Navigator.of(context). If you check its source code:

    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!;
    }
    

    How to solve this?

    You have to use the context to get the Navigator before the dispose method. As explained in the error, you should create a reference to the object (in the didChangeDependencies method for example) and use it latter.

    Here is a concrete example:

    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 ...;
      }
    }
    

    Why not use the initState method rather than didChangeDependencies? Because, as much as the context is not longer valid in dispose, the context is not yet valid in initState because the widget has not yet been inserted in the widget tree.