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

Flutter应用迟到判定函数重复执行致计数器异常问题求助

穆斯林礼拜打卡App迟到计数器重复增长问题

我开发了一款面向穆斯林的礼拜打卡App,用于记录用户是否完成礼拜打卡。编写了latecheck函数,在用户每次打开App时检查是否存在礼拜迟到情况:例如当前处于祖尔(Zohor)礼拜时段,若用户此时打开App但未完成晨礼(Subuh)打卡,则调用迟到函数,在SharedPreferences中以“subuh”为键存储整数2(2代表迟到,1代表提前打卡,0为默认值)。

目前功能基本正常,但存在核心问题:每当触发迟到判定时,迟到计数器会持续增长,尽管预期该函数仅在首次判定迟到时执行一次。刚接触Flutter,代码编写较为繁琐,已为此困扰多日。


页面检查逻辑代码(latecheck函数)

void latecheck(){
    int subuh = Provider.of<checkinlist>(context, listen: false).getSubuh();
    int zohor = Provider.of<checkinlist>(context, listen: false).getZohor();
    int asar = Provider.of<checkinlist>(context, listen: false).getAsar();
    int maghrib = Provider.of<checkinlist>(context, listen: false).getMaghrib();
    int isyak = Provider.of<checkinlist>(context, listen: false).getIsyak();
    bool checksubuh = Provider.of<checkinlist>(context, listen: false).getCheckSubuh();
    bool checkzohor = Provider.of<checkinlist>(context, listen: false).getCheckZohor();
    bool checkasar = Provider.of<checkinlist>(context, listen: false).getCheckAsar();
    bool checkmaghrib = Provider.of<checkinlist>(context, listen: false).getCheckMaghrib();
    bool checkisyak = Provider.of<checkinlist>(context, listen: false).getCheckIsyak();

    switch(subuh){
      case 0:
        if (prayerTimes.currentPrayer().index >= 1  ) {
          if (checksubuh == false) {
           setState(() {
             Provider.of<checkinlist>(context, listen: false).SubuhChecked();
             Provider.of<checkinlist>(context, listen: false).decreasehealth();
             Provider.of<checkinlist>(context, listen: false).subuhlate();
           });
          }
        }
        break;
    }
    switch(zohor){
      case 0:
        if (prayerTimes.currentPrayer().index >= 4  ) {
          if (checkzohor == false ) {
            setState(() {
              Provider.of<checkinlist>(context, listen: false).zohorlate();
              Provider.of<checkinlist>(context, listen: false).decreasehealth();
              Provider.of<checkinlist>(context, listen: false).ZohorChecked();
            });
          }
        }
        break;
        }
    switch(asar){
      case 0:
        if (prayerTimes.currentPrayer().index >= 5 ) {
          if (checkasar == false ) {
            setState(() {
              Provider.of<checkinlist>(context, listen: false).asarlate();
              Provider.of<checkinlist>(context, listen: false).decreasehealth();
              Provider.of<checkinlist>(context, listen: false).AsarChecked();
            });
          }
        }
        break;
    }
    switch(maghrib){
      case 0:
        if (prayerTimes.currentPrayer().index >= 6  ) {
          if (checkmaghrib == false) {
            setState(() {
              Provider.of<checkinlist>(context, listen: false).maghriblate();
              Provider.of<checkinlist>(context, listen: false).decreasehealth();
              Provider.of<checkinlist>(context, listen: false).MaghribChecked();
            });
          }
        }
        break;
    }
    switch(isyak){
      case 0:
        if (DateTime.now().hour >= 23  ) {
          if (checkisyak == false) {
            setState(() {
              Provider.of<checkinlist>(context, listen: false).maghriblate();
              Provider.of<checkinlist>(context, listen: false).decreasehealth();
              Provider.of<checkinlist>(context, listen: false).MaghribChecked();
            });
          }
        }
        break;

      default:
        break;
    }

  }

礼拜状态存储函数(以Subuh为例)

Future<void> subuhcheckin() async {
    final prefs = await SharedPreferences.getInstance();
    subuh = 1;
    prefs.setInt('subuh', subuh);
    notifyListeners();
  }

  Future<void> subuhlate() async {
    final prefs = await SharedPreferences.getInstance();
    subuh = 2;
    prefs.setInt('subuh', subuh);
    increaseLateCounter();
    increaseLateSubuh();
    notifyListeners();
  }

  Future<void> loadSubuh()  async {
    final prefs = await SharedPreferences.getInstance();
    subuh = (prefs.getInt('subuh') ?? 0);
    notifyListeners();
  }

  int getSubuh() {
    loadSubuh();
    return subuh;
  }

迟到计数器相关函数

Future<void> increaseLateCounter()  async {
    final prefs = await SharedPreferences.getInstance();
    late = (prefs.getInt('late') ?? 0) + 1;
    prefs.setInt('late', late);
    notifyListeners();
  }


  Future<void> loadLate()  async {
    final prefs = await SharedPreferences.getInstance();
    late = (prefs.getInt('late') ?? 0);
    notifyListeners();
  }

  int getLate() {
    loadLate();
    return late;
  }

  Future<void> increaseLateSubuh()  async {
    final prefs = await SharedPreferences.getInstance();
    latesubuh = (prefs.getInt('latesubuh') ?? 0) + 1;
    prefs.setInt('latesubuh', latesubuh);
    notifyListeners();
  }


  Future<void> loadLateSubuh()  async {
    final prefs = await SharedPreferences.getInstance();
    latesubuh = (prefs.getInt('latesubuh') ?? 0);
    notifyListeners();
  }

  int getLateSubuh() {
    loadLateSubuh();
    return latesubuh;
  }

问题根源分析

  1. 异步加载逻辑错误:getSubuh()这类同步方法中直接调用异步的loadSubuh(),会导致返回的subuh值是未更新的旧值,判定逻辑会反复认为状态为0,重复触发迟到处理。
  2. 重复判定触发:迟到判定依赖checksubuh和subuh两个状态,二者未同步,且未对subuh是否已经标记为迟到(值为2)做判断,导致每次打开App都会重新执行计数增长。
  3. Isyak处理代码错误:Isyak的迟到逻辑中错误调用了maghriblate()和MaghribChecked(),不仅会导致Isyak状态错误,还会错误增长Maghrib的迟到计数。

解决方案

1. 修复异步加载逻辑,提前初始化数据

将Provider中的数据加载逻辑提前到初始化阶段,避免在判定时才异步加载导致状态不一致:

class CheckinList extends ChangeNotifier {
  int subuh = 0;
  int zohor = 0;
  int asar = 0;
  int maghrib = 0;
  int isyak = 0;
  int late = 0;
  int latesubuh = 0;

  // 初始化时预加载所有数据
  CheckinList() {
    _loadAllData();
  }

  Future<void> _loadAllData() async {
    await Future.wait([
      loadSubuh(),
      loadZohor(),
      loadAsar(),
      loadMaghrib(),
      loadIsyak(),
      loadLate(),
      loadLateSubuh(),
    ]);
  }

  // 修改get方法,直接返回已加载的变量,不再调用异步方法
  int getSubuh() => subuh;
  int getZohor() => zohor;
  // 其他get方法同理
}

2. 优化迟到判定逻辑,避免重复触发

简化判定条件,直接通过subuh状态值判断是否需要触发迟到处理,且确保一旦标记为迟到就不再重复执行:

void latecheck() {
  final checkinProvider = Provider.of<CheckinList>(context, listen: false);

  // 晨礼迟到判定:当前时段在Zohor及之后,且未标记任何状态
  if (checkinProvider.getSubuh() == 0 && prayerTimes.currentPrayer().index >= 1) {
    checkinProvider.subuhlate();
    checkinProvider.SubuhChecked();
    checkinProvider.decreasehealth();
  }

  // 祖尔礼拜迟到判定
  if (checkinProvider.getZohor() == 0 && prayerTimes.currentPrayer().index >= 4) {
    checkinProvider.zohorlate();
    checkinProvider.decreasehealth();
    checkinProvider.ZohorChecked();
  }

  // Asar礼拜迟到判定
  if (checkinProvider.getAsar() == 0 && prayerTimes.currentPrayer().index >= 5) {
    checkinProvider.asarlate();
    checkinProvider.decreasehealth();
    checkinProvider.AsarChecked();
  }

  // Maghrib礼拜迟到判定
  if (checkinProvider.getMaghrib() == 0 && prayerTimes.currentPrayer().index >= 6) {
    checkinProvider.maghriblate();
    checkinProvider.decreasehealth();
    checkinProvider.MaghribChecked();
  }

  // Isyak礼拜迟到判定:修复调用错误
  if (checkinProvider.getIsyak() == 0 && DateTime.now().hour >= 23) {
    checkinProvider.isyaklate(); // 替换原错误的maghriblate()
    checkinProvider.decreasehealth();
    checkinProvider.IsyakChecked(); // 替换原错误的MaghribChecked()
  }
}

3. 给迟到函数增加状态校验

修改subuhlate()这类方法,只有当当前状态不是迟到时,才执行计数增长:

Future<void> subuhlate() async {
  final prefs = await SharedPreferences.getInstance();
  // 仅当未标记迟到时执行
  if (subuh != 2) {
    subuh = 2;
    await prefs.setInt('subuh', subuh);
    await increaseLateCounter();
    await increaseLateSubuh();
    notifyListeners();
  }
}

4. 简化重复代码(可选)

将每个礼拜的判定逻辑封装为通用方法,提升代码可维护性:

void _checkLate({
  required int prayerStatus,
  required bool shouldTrigger,
  required Future<void> Function() markLate,
  required Future<void> Function() markChecked,
  required CheckinList provider,
}) {
  if (prayerStatus == 0 && shouldTrigger) {
    markLate();
    markChecked();
    provider.decreasehealth();
  }
}

// 调用示例
_checkLate(
  prayerStatus: checkinProvider.getSubuh(),
  shouldTrigger: prayerTimes.currentPrayer().index >= 1,
  markLate: checkinProvider.subuhlate,
  markChecked: checkinProvider.SubuhChecked,
  provider: checkinProvider,
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 07:35:22