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

Flutter如何在日期变更时自动刷新Riverpod FutureProvider数据源列表?

问题解答

你的猜测是对的——手机进入休眠状态(比如Android的Doze模式、iOS的低功耗模式)时,后台运行的定时Stream会被系统限制暂停,导致Stream.periodic无法按预期每分钟执行日期检查,自然触发不了日期变化的事件,所以次日打开应用时还是旧数据。

解决办法

1. 监听应用前后台切换,主动触发检查

不需要依赖后台定时任务,而是在应用从后台回到前台时,主动对比当前日期和上次存储的日期,判断是否需要刷新数据。

先创建一个监听应用生命周期的Provider:

final appLifecycleProvider = StreamProvider<AppLifecycleState>((ref) {
  final controller = StreamController<AppLifecycleState>();
  final observer = _LifecycleObserver(controller);
  WidgetsBinding.instance.addObserver(observer);
  
  ref.onDispose(() {
    WidgetsBinding.instance.removeObserver(observer);
    controller.close();
  });
  
  return controller.stream;
});

class _LifecycleObserver extends WidgetsBindingObserver {
  final StreamController<AppLifecycleState> controller;
  
  _LifecycleObserver(this.controller);
  
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    controller.add(state);
  }
}

再修改你的数据Provider,结合生命周期和日期检查:

final findTodayDashboardTaskProvider = FutureProvider.autoDispose<List<GdTask>>((ref) async {
  // 监听应用回到前台,触发刷新
  ref.listen(appLifecycleProvider, (_, state) {
    if (state == AppLifecycleState.resumed) {
      ref.invalidateSelf();
    }
  });

  const dayCheckerKey = 'dayCheckerTimestamp';
  final prefs = ref.read(sharedPrefsProvider);
  final now = DateTime.now();
  final lastTimestampStr = prefs.getString(dayCheckerKey);

  // 检查日期是否变化
  if (lastTimestampStr != null) {
    final lastDate = DateTime.parse(lastTimestampStr);
    final dayChanged = now.day != lastDate.day || now.month != lastDate.month || now.year != lastDate.year;
    if (dayChanged) {
      prefs.setString(dayCheckerKey, now.toIso8601String());
    }
  } else {
    prefs.setString(dayCheckerKey, now.toIso8601String());
  }

  // 原有加载当日数据的逻辑
  return await loadTodayTasks();
});

2. 简化StreamProvider,仅在应用活跃时运行

如果还是想用Stream实现,可以让定时检查只在应用处于活跃状态时执行,避免被系统后台限制:

final newDayProvider = StreamProvider<DateTime?>((ref) async* {
  const dayCheckerKey = 'dayCheckerTimestamp';
  final prefs = ref.read(sharedPrefsProvider);
  final lifecycleStream = ref.watch(appLifecycleProvider);

  // 初始化时先检查一次
  final initNow = DateTime.now();
  final initLastStr = prefs.getString(dayCheckerKey);
  if (initLastStr != null) {
    final initLastDate = DateTime.parse(initLastStr);
    if (initNow.day != initLastDate.day || initNow.month != initLastDate.month || initNow.year != initLastDate.year) {
      prefs.setString(dayCheckerKey, initNow.toIso8601String());
      yield initNow;
    }
  } else {
    prefs.setString(dayCheckerKey, initNow.toIso8601String());
  }

  // 监听生命周期,仅在活跃时执行定时检查
  await for (final state in lifecycleStream) {
    if (state == AppLifecycleState.resumed) {
      // 回到前台立即检查
      final resumeNow = DateTime.now();
      final resumeLastStr = prefs.getString(dayCheckerKey);
      if (resumeLastStr != null) {
        final resumeLastDate = DateTime.parse(resumeLastStr);
        if (resumeNow.day != resumeLastDate.day || resumeNow.month != resumeLastDate.month || resumeNow.year != resumeLastDate.year) {
          prefs.setString(dayCheckerKey, resumeNow.toIso8601String());
          yield resumeNow;
        }
      }

      // 活跃期间每分钟检查一次,日期变化后停止定时
      await for (final _ in Stream.periodic(const Duration(minutes: 1))) {
        final checkNow = DateTime.now();
        final checkLastStr = prefs.getString(dayCheckerKey);
        if (checkLastStr != null) {
          final checkLastDate = DateTime.parse(checkLastStr);
          final changed = checkNow.day != checkLastDate.day || checkNow.month != checkLastDate.month || checkNow.year != checkLastDate.year;
          if (changed) {
            prefs.setString(dayCheckerKey, checkNow.toIso8601String());
            yield checkNow;
            break;
          }
        }
      }
    }
  }
});

3. 轻量方案:Provider初始化时直接检查日期

如果你的场景只需要用户打开应用时刷新数据,不需要后台实时监听,直接在数据Provider初始化时检查日期即可:

final findTodayDashboardTaskProvider = FutureProvider.autoDispose<List<GdTask>>((ref) async {
  const dayCheckerKey = 'dayCheckerTimestamp';
  final prefs = ref.read(sharedPrefsProvider);
  final now = DateTime.now();
  final lastTimestampStr = prefs.getString(dayCheckerKey);

  bool needRefresh = false;
  if (lastTimestampStr != null) {
    final lastDate = DateTime.parse(lastTimestampStr);
    needRefresh = now.day != lastDate.day || now.month != lastDate.month || now.year != lastDate.year;
  } else {
    needRefresh = true;
  }

  if (needRefresh) {
    prefs.setString(dayCheckerKey, now.toIso8601String());
    // 这里可以添加清除缓存等逻辑
  }

  // 加载当日数据
  return await loadTodayTasks();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 13:03:17