I want to build a math app with Flutter. It should have basic functions. However, I am facing the following issue: when my timer runs out and I am directed to the next page, the following error message appears:
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.
class MathFunctionScreen extends StatefulWidget {
static String id = "MathFunctionScreen";
const MathFunctionScreen({Key? key}) : super(key: key);
@override
State<MathFunctionScreen> createState() => _MathFunctionScreenState();
}
class _MathFunctionScreenState extends State<MathFunctionScreen>
with TickerProviderStateMixin {
DataBase db = DataBase();
late int myTime;
late int timeStamp;
@override
void initState() {
print("initState() wurde ausgeführt");
Provider.of<NumberGenerator>(context, listen: false).ctrl;
myOnChange();
Provider.of<NumberGenerator>(context, listen: false).mathCorrectAnswer;
Provider.of<NumberGenerator>(context, listen: false).operator;
Provider.of<NumberGenerator>(context, listen: false).setDatabase();
Provider.of<TimerProvider>(context, listen: false).startTimer(context);
animationController;
offsetAnimation;
animationController.forward();
super.initState();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
@override
void dispose() {
Provider.of<TimerProvider>(context, listen: false).timer.cancel();
Provider.of<NumberGenerator>(context, listen: false)
.ctrl
.removeListener(() {});
animationController.dispose();
super.dispose();
}
late AnimationController animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 600),
);
late Animation<Offset> offsetAnimation =
Tween<Offset>(begin: const Offset(-3, 0), end: Offset.zero).animate(
CurvedAnimation(parent: animationController, curve: Curves.easeIn),
);
void myOnChange() {
Provider.of<NumberGenerator>(context, listen: false)
.textFieldTextChange(animationController);
}
void _updateText(String text) async {
String answer = (await Provider.of<NumberGenerator>(context, listen: false)
.mathCorrectAnswer)
.toString();
int answerLength = answer.length;
setState(() {
if (Provider.of<NumberGenerator>(context, listen: false)
.ctrl
.text
.length <
answerLength) {
Provider.of<NumberGenerator>(context, listen: false).ctrl.text =
Provider.of<NumberGenerator>(context, listen: false).ctrl.text +
text;
}
});
}
@override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
And the following method is in a class called NumberGenerator, which inherits ChangeNotifier
void textFieldTextChange(AnimationController animationController) {
ctrl.addListener(
() async {
String mathCorrectAnswerString = (await mathCorrectAnswer).toString();
// user hat die zulässige Länge erreicht
if (ctrl.text.length >= mathCorrectAnswerString.length) {
//this delayed ist, damit die letzte zahl noch gesehen werden kann!
await Future.delayed(const Duration(milliseconds: 200));
checkAnswer(ctrl.text == mathCorrectAnswerString);
if (ctrl.text == mathCorrectAnswerString) {
updateDatabaseResult();
await Future.delayed(const Duration(milliseconds: 20));
wrongAnswers.clear();
wrongAnswers.add(addRightIcon);
notifyListeners();
await Future.delayed(const Duration(milliseconds: 500));
wrongAnswers.clear();
updateLeftAndRightInt();
setDatabase();
animationController.reset();
animationController.forward();
notifyListeners();
} else if (ctrl.text != mathCorrectAnswerString &&
(wrongAnswers.length <= 1)) {
updateDatabaseResult();
wrongAnswers.add(addWrongIcon);
notifyListeners();
await Future.delayed(const Duration(milliseconds: 500));
ctrl.clear();
} else {
updateDatabaseResult();
wrongAnswers.add(addWrongIcon);
notifyListeners();
await Future.delayed(const Duration(seconds: 1));
updateLeftAndRightInt();
setDatabase();
animationController.reset();
animationController.forward();
wrongAnswers.clear();
notifyListeners();
}
}
And the following class contains the timer. When it has expired, I am redirected to the new page and receive the error message:
class TimerProvider extends ChangeNotifier {
late Timer timer;
late int value;
String collectionTime = "Time";
String key = "myTime";
startTimer(BuildContext context) {
timer = Timer.periodic(
const Duration(seconds: 1),
(timer) async {
if (value > 0) {
value--;
notifyListeners();
} else if (value <= 0) {
final QuerySnapshot<Map<String, dynamic>> documents =
await FirebaseFirestore.instance
.collection('Funktionen')
.where('result', isEqualTo: "")
.get();
final List<Future<void>> deleteFutures = [];
for (final QueryDocumentSnapshot<Map<String, dynamic>> document
in documents.docs) {
deleteFutures.add(document.reference.delete());
}
await Future.wait(deleteFutures);
timer.cancel();
if (context.mounted) {
Navigator.pushReplacementNamed(context, UserResultScreen.id);
// Navigator.pushNamed(context, UserResultScreen.id);
}
}
},
);
}
Can someone help me with this ?
It`s because you called the inherited widget inside dispose of, in you're sample this is Provider.of<"Type">
You need to do this way:
class _MathFunctionScreenState extends State<MathFunctionScreen>
with TickerProviderStateMixin {
...
NumberGenerator numberGenerator;
TimerProvider timerProvider;
...
@override
void didChangeDependencies() {
numberGenerator = Provider.of<NumberGenerator>;
timerProvider = Provider.of<TimerProvider>;
super.didChangeDependencies();
}
@override
void dispose() {
timerProvider.timer.cancel();
numberGenerator.ctrl.removeListener(() {});
animationController.dispose();
super.dispose();
}
}