Flutter中Stream.periodic能否跳过事件?示例写法是否最优?
问题解答
1. Stream.periodic中避免分发事件的可行性
可以实现。你当前的做法是在Stream.periodic的生成函数中返回null,再通过外层的await for过滤掉null值来避免无效事件分发,这是一种可行的方式。不过还有更简洁的实现思路:直接通过自定义异步生成器(async*)来控制事件的发射,不需要依赖Stream.periodic生成多余的null事件。
2. 当前RiverPod实现的优化空间
你的实现可以正常工作,但存在几个可以优化的点:
- 不必要的IO操作:每分钟都会写入SharedPreferences,哪怕日期没有变化,频繁的本地存储操作会浪费资源
- 冗余的事件过滤:
Stream.periodic每次都会生成事件(哪怕是null),再在外层过滤,增加了不必要的事件流转 - 初始逻辑缺失:首次运行时,因为没有历史时间戳,不会触发事件,如果需要首次启动就检测是否是新的一天(比如APP跨天启动),当前逻辑无法覆盖
优化后的实现方案
final newDayProvider = StreamProvider<DateTime>((ref) async* { const dayCheckerKey = 'dayCheckerTimestamp'; final sharedPrefs = ref.read(sharedPrefsProvider); // 首次启动先检查是否跨天 final lastTimestampStr = sharedPrefs.getString(dayCheckerKey); final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); if (lastTimestampStr != null) { final lastDay = DateTime.parse(lastTimestampStr); final lastCheckDay = DateTime(lastDay.year, lastDay.month, lastDay.day); if (lastCheckDay.isBefore(today)) { yield today; sharedPrefs.setString(dayCheckerKey, now.toIso8601String()); } } else { // 首次运行,记录当前日期 sharedPrefs.setString(dayCheckerKey, now.toIso8601String()); } // 定时检查日期变化 while (true) { await Future.delayed(const Duration(minutes: 1)); final currentNow = DateTime.now(); final currentDay = DateTime(currentNow.year, currentNow.month, currentNow.day); final latestTimestampStr = sharedPrefs.getString(dayCheckerKey); if (latestTimestampStr == null) continue; final lastCheckDay = DateTime.parse(latestTimestampStr); final lastDay = DateTime(lastCheckDay.year, lastCheckDay.month, lastCheckDay.day); if (lastDay.isBefore(currentDay)) { yield currentDay; sharedPrefs.setString(dayCheckerKey, currentNow.toIso8601String()); } } });
优化点说明
- 减少IO操作:只有当日期确实变化时,才更新SharedPreferences
- 精准控制事件:通过
while(true)+Future.delayed替代Stream.periodic,只在需要时发射事件,避免冗余的null事件 - 补充初始检查:APP启动时先判断是否跨天,覆盖了跨天启动的场景
- 日期比较更严谨:通过提取年/月/日创建纯日期对象进行比较,避免时分秒干扰
内容的提问来源于stack exchange,提问作者Davout
相关产品推荐
相关产品推荐

