Reputation: 524
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);
Upvotes: 13
Views: 36461
Reputation: 183
Just a few hours ago I also faced the same problem where I was getting the error exactly the same as you mentioned in your problem. When I tried to know what exactly is going wrong even on calling a simple navigator, I came to know that I was calling the context of a widget which is already dispose. Exactly what happened was, I was building a quiz app where the user was to be redirected to the result screen when time is over. So I was calling the navigator as
Future.delayed(Duration(seconds: quiz.quizDuration)).then((value) {
// go to result screen when time is over
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const ResultScreen()));
});
But the user would also be redirected to the result screen if he answers all questions before time. So this widget would be already disposed and our delayed function will complete after the time and try to use the context of a disposed widget.. So that's the error.
I solved it by checking if the state persists. If widget is still there dispose it, if it is already disposed do nothing.
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()));
}
});
This is how I solved my problem.. So I guess you can also solve your problem by putting your navigator code inside
if(mounted)
{
// navigator....
}
Upvotes: 5
Reputation: 1
I had a similar problem caused by the fact that I forgot to call timer.cancel() within a Timer.periodic() function.
Upvotes: 0
Reputation: 3594
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!;
}
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.
Upvotes: 42