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

使用Riverpod+ChangeNotifier开发计时器,Widget文本不更新问题

问题原因
  1. 误用ref.read而非ref.watch:ref.read仅一次性读取Provider的值,不会监听后续状态变化。即便TimerNotifier调用了notifyListeners(),Widget也不会触发重建更新UI。
  2. 直接访问私有成员:_seconds是TimerNotifier的私有变量,直接在UI中访问既不符合封装原则,也无法让Riverpod追踪状态变化关联。
  3. 调用私有方法:_startTimer作为私有方法,直接通过notifier调用虽能执行,但违背类的封装设计规范。
修复步骤

1. 调整TimerNotifier类,提供公共访问接口

给私有变量_seconds添加公共getter,同时将私有方法改为公共方法:

class TimerNotifier extends ChangeNotifier {
  final countDownTimer = <ClickTimer>[];

  int _seconds = 20;
  Timer? _timer;

  // 公共getter,对外暴露秒数
  int get seconds => _seconds;

  // 改为公共方法,允许外部调用
  void startTimer() {
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      if (_seconds > 0) {
        _seconds--;
      } else {
        _timer!.cancel();
      }
      notifyListeners();
    });
  }

  void increaseTimer() {
    _seconds += 3;
    notifyListeners();
  }

  void reduceTimer() {
    _seconds -= 3;
    notifyListeners();
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }
}

2. 修改UI代码,使用ref.watch监听状态

用ref.watch获取TimerNotifier实例,通过公共getter读取秒数,调用公共方法启动计时器:

class CountDownTimer extends ConsumerWidget {
  const CountDownTimer({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 用ref.watch监听Provider,状态变化时自动重建Widget
    final timerNotifier = ref.watch(timerProvider);
    return Column(children: [
      Text("${timerNotifier.seconds}"),
      ElevatedButton(
          onPressed: () {
            ref.read(timerProvider.notifier).startTimer();
          },
          child: const Text("Start timer"))
    ]);
  }
}
补充说明
  • ref.watch是Riverpod监听状态变化的核心方法,当Provider内部调用notifyListeners()时,使用ref.watch的Widget会自动重建,实现UI更新。
  • 保持类的封装性,通过公共方法和getter暴露必要操作与状态,既符合面向对象设计,也能让Riverpod正确追踪状态变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 04:45:14