使用Riverpod+ChangeNotifier开发计时器,Widget文本不更新问题
问题原因
- 误用
ref.read而非ref.watch:ref.read仅一次性读取Provider的值,不会监听后续状态变化。即便TimerNotifier调用了notifyListeners(),Widget也不会触发重建更新UI。 - 直接访问私有成员:
_seconds是TimerNotifier的私有变量,直接在UI中访问既不符合封装原则,也无法让Riverpod追踪状态变化关联。 - 调用私有方法:
_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
相关产品推荐
相关产品推荐

