Flutter数学应用跳转页面时出现组件祖先查找不安全错误求助
Flutter计时器跳转页面时的断言错误解决
问题描述
开发基础功能的Flutter数学应用时,计时器结束跳转新页面会抛出断言错误:Looking up a deactivated widget's ancestor is unsafe,提示需在didChangeDependencies()中调用dependOnInheritedWidgetOfExactType()保存引用。
核心原因
TimerProvider直接持有BuildContext引用,异步操作完成后可能访问已失活的contextdispose()方法中直接通过context获取Provider,此时widget已处于销毁状态NumberGenerator的listener中存在异步延迟操作,widget销毁后仍可能执行状态通知或资源操作
修复方案
1. 移除TimerProvider对BuildContext的依赖
修改TimerProvider,改用状态通知替代直接持有context,由页面自身监听状态变化处理跳转:
class TimerProvider extends ChangeNotifier { late Timer timer; late int value; String collectionTime = "Time"; String key = "myTime"; bool _isTimeUp = false; bool get isTimeUp => _isTimeUp; startTimer() { 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(); _isTimeUp = true; notifyListeners(); } }, ); } void resetTimeUp() { _isTimeUp = false; } }
在MathFunctionScreen的build方法中监听状态并处理跳转:
@override Widget build(BuildContext context) { final timerProvider = Provider.of<TimerProvider>(context); // 监听时间结束状态,确保context有效时跳转 if (timerProvider.isTimeUp) { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.pushReplacementNamed(context, UserResultScreen.id); timerProvider.resetTimeUp(); }); } double height = MediaQuery.of(context).size.height; double width = MediaQuery.of(context).size.width; // 原有build代码... }
2. 提前保存Provider引用,避免dispose时访问失效context
在MathFunctionScreen的State中,提前在didChangeDependencies中保存Provider引用:
class _MathFunctionScreenState extends State<MathFunctionScreen> with TickerProviderStateMixin { DataBase db = DataBase(); late int myTime; late int timeStamp; late TimerProvider _timerProvider; late NumberGenerator _numberGenerator; @override void didChangeDependencies() { super.didChangeDependencies(); // 提前保存Provider引用 _timerProvider = Provider.of<TimerProvider>(context, listen: false); _numberGenerator = Provider.of<NumberGenerator>(context, listen: false); } @override void initState() { print("initState() wurde ausgeführt"); _numberGenerator.ctrl; myOnChange(); _numberGenerator.mathCorrectAnswer; _numberGenerator.operator; _numberGenerator.setDatabase(); _timerProvider.startTimer(); // 不再传递context animationController; offsetAnimation; animationController.forward(); super.initState(); } @override void dispose() { // 使用提前保存的引用操作 _timerProvider.timer.cancel(); _numberGenerator.ctrl.removeListener(() {}); _numberGenerator.dispose(); animationController.dispose(); super.dispose(); } // 其他方法改用保存的引用 void myOnChange() { _numberGenerator.textFieldTextChange(animationController); } void _updateText(String text) async { String answer = (await _numberGenerator.mathCorrectAnswer).toString(); int answerLength = answer.length; setState(() { if (_numberGenerator.ctrl.text.length < answerLength) { _numberGenerator.ctrl.text = _numberGenerator.ctrl.text + text; } }); } }
3. 防止NumberGenerator异步操作在widget销毁后执行
给NumberGenerator添加销毁标记,在异步操作中检查状态:
class NumberGenerator extends ChangeNotifier { // 原有成员变量... bool _isDisposed = false; void textFieldTextChange(AnimationController animationController) { ctrl.addListener( () async { if (_isDisposed) return; String mathCorrectAnswerString = (await mathCorrectAnswer).toString(); if (ctrl.text.length >= mathCorrectAnswerString.length) { await Future.delayed(const Duration(milliseconds: 200)); if (_isDisposed) return; checkAnswer(ctrl.text == mathCorrectAnswerString); if (ctrl.text == mathCorrectAnswerString) { updateDatabaseResult(); await Future.delayed(const Duration(milliseconds: 20)); if (_isDisposed) return; wrongAnswers.clear(); wrongAnswers.add(addRightIcon); if (!_isDisposed) notifyListeners(); await Future.delayed(const Duration(milliseconds: 500)); if (_isDisposed) return; wrongAnswers.clear(); updateLeftAndRightInt(); setDatabase(); animationController.reset(); animationController.forward(); if (!_isDisposed) notifyListeners(); } else if (ctrl.text != mathCorrectAnswerString && (wrongAnswers.length <= 1)) { updateDatabaseResult(); wrongAnswers.add(addWrongIcon); if (!_isDisposed) notifyListeners(); await Future.delayed(const Duration(milliseconds: 500)); if (_isDisposed) return; ctrl.clear(); } else { updateDatabaseResult(); wrongAnswers.add(addWrongIcon); if (!_isDisposed) notifyListeners(); await Future.delayed(const Duration(seconds: 1)); if (_isDisposed) return; updateLeftAndRightInt(); setDatabase(); animationController.reset(); animationController.forward(); wrongAnswers.clear(); if (!_isDisposed) notifyListeners(); } } }, ); } @override void dispose() { _isDisposed = true; ctrl.dispose(); // 销毁TextEditingController super.dispose(); } }
内容的提问来源于stack exchange,提问作者aycha
相关产品推荐
相关产品推荐

