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

Riverpod报错'!_didChangeDependency':后台计时功能异常排查

错误原因分析与解决方案

错误原因

在Timer.periodic的异步回调中调用ref.watch(isBackgroundBoolProvider)违反了Riverpod的生命周期约束:

  • Riverpod要求Provider对其他Provider的监听必须在build方法内部或Provider的生命周期钩子(如onDispose)中执行。
  • Timer回调属于异步独立执行的逻辑,不属于Provider的重建流程。当isBackgroundBoolProvider的值更新时,TotalFocusTime Provider的依赖发生变化,但此时Timer回调中的ref操作发生在Provider完成重建之前,直接触发!_didChangeDependency错误。

解决方案

方案1:自动根据APP状态启停计时器(无需手动触发)

修改TotalFocusTime Provider,在build方法中直接监听后台状态,自动控制计时器的启停:

@riverpod
class TotalFocusTime extends _$TotalFocusTime {
  Timer? timer;

  @override
  int build() {
    // 监听APP后台状态,状态变化时自动重建Provider
    final isBackground = ref.watch(isBackgroundBoolProvider);

    // 根据当前状态控制计时器
    if (isBackground) {
      _startTimer();
    } else {
      _stopTimer();
    }

    // Provider销毁时清理计时器,避免内存泄漏
    ref.onDispose(() {
      timer?.cancel();
    });

    return 0;
  }

  void _startTimer() {
    // 避免重复启动计时器
    if (timer?.isActive ?? false) return;
    timer = Timer.periodic(const Duration(seconds: 1), (_) {
      state++;
    });
  }

  void _stopTimer() {
    timer?.cancel();
    timer = null;
  }
}

说明:

  • 当APP前后台状态切换时,isBackgroundBoolProvider更新,触发TotalFocusTime Provider重建,自动处理计时器启停。
  • 无需手动调用startFocusTimer,计时器完全由APP状态驱动。

方案2:保留按钮触发逻辑(仅点击按钮后开始计时)

如果需要保留“点击按钮才启动计时”的需求,可以增加一个内部状态控制是否允许计时:

@riverpod
class TotalFocusTime extends _$TotalFocusTime {
  Timer? timer;
  bool _isTimerEnabled = false; // 标记是否允许计时

  @override
  int build() {
    final isBackground = ref.watch(isBackgroundBoolProvider);

    if (_isTimerEnabled) {
      // 仅在允许计时的状态下,根据APP后台状态控制计时器
      if (isBackground) {
        _startTimer();
      } else {
        _stopTimer();
      }
    }

    ref.onDispose(() {
      timer?.cancel();
    });

    return 0;
  }

  void startFocusTimer() {
    _isTimerEnabled = true;
    // 手动触发Provider重建,让build方法处理计时器逻辑
    ref.invalidateSelf();
  }

  void _startTimer() {
    if (timer?.isActive ?? false) return;
    timer = Timer.periodic(const Duration(seconds: 1), (_) {
      state++;
    });
  }

  void _stopTimer() {
    timer?.cancel();
    timer = null;
  }
}

说明:

  • 点击按钮调用startFocusTimer后,才会开启计时逻辑。
  • 后续APP状态切换时,依然会自动暂停/恢复后台计时。

额外注意事项

  • 确保isBackgroundBoolProvider的更新逻辑正确(你已确认该值更新正常,此点可忽略)。
  • 计时器的清理必须通过ref.onDispose实现,避免页面销毁后计时器继续运行导致内存泄漏。

内容的提问来源于stack exchange,提问作者Milan Labus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 12:30:25