You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter数学应用跳转页面时出现组件祖先查找不安全错误求助

Flutter计时器跳转页面时的断言错误解决

问题描述

开发基础功能的Flutter数学应用时,计时器结束跳转新页面会抛出断言错误:Looking up a deactivated widget's ancestor is unsafe,提示需在didChangeDependencies()中调用dependOnInheritedWidgetOfExactType()保存引用。

核心原因

  1. TimerProvider直接持有BuildContext引用,异步操作完成后可能访问已失活的context
  2. dispose()方法中直接通过context获取Provider,此时widget已处于销毁状态
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:30:02