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; }
问题根源分析
- 异步加载逻辑错误:
getSubuh()这类同步方法中直接调用异步的loadSubuh(),会导致返回的subuh值是未更新的旧值,判定逻辑会反复认为状态为0,重复触发迟到处理。 - 重复判定触发:迟到判定依赖
checksubuh和subuh两个状态,二者未同步,且未对subuh是否已经标记为迟到(值为2)做判断,导致每次打开App都会重新执行计数增长。 - 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
相关产品推荐
相关产品推荐

