如何在App中实现按指定时间间隔(每周一次)展示卡片
原有写法的问题
你现在的实现从根上就跑不通,核心问题有3个:
Future.delayed是进程内存级的计时,只要App被杀、后台被系统回收,计时直接清零。正常用户不可能连续7天不杀App、不让系统回收后台进程,这个延时任务99%的场景下根本触发不了。就算用户不杀App,只要进了主页切去其他页面,7天后照样会弹,根本不管用户当前在不在主页。- 生命周期写法违规:
super.initState()必须写在initState方法的最开头,你放在延时任务后面,很容易出现组件未初始化完成就调用context、触发框架层报错。 - 没有状态持久化和去重:就算计时成功触发了,只要用户下次重进App,又会重新注册一个7天的延时任务,完全做不到“每7天展示一次”的规则,甚至页面rebuild的时候还可能重复注册多个延时任务,导致一次弹好几个卡片。
正确实现思路
长周期的定时触发逻辑,绝对不能靠内存里跑延时,正确逻辑是:
- 用本地轻量KV存储,记录上一次成功展示卡片的时间戳
- 每次进入主页、或者App从后台切回前台的时候,读取本地存的上次展示时间,和当前时间做差
- 如果时间差满7天,就弹卡片,弹完(或者用户看完卡片关闭后)把当前时间更新到本地存储,作为下一轮7天周期的起点
可直接复用的代码示例
首先接入项目里常用的轻量本地KV存储(比如shared_preferences,你用其他存储方案逻辑也完全一致),然后按下面的写法实现:
import 'package:flutter/material.dart'; // 替换为你项目实际使用的本地存储引入 import 'package:shared_preferences/shared_preferences.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { // 本地存储用的key,存上次展示卡片的时间戳(毫秒级) static const _lastShowCardKey = 'last_show_weekly_card_ts'; // 7天间隔对应的毫秒数 static const _weekInMs = 7 * 24 * 60 * 60 * 1000; @override void initState() { // 必须第一行调用父类初始化方法 super.initState(); // 等页面首帧渲染完成后再检查触发条件,避免刚进页面就弹、或者组件未挂载报错 WidgetsBinding.instance.addPostFrameCallback((_) => _checkAndShowCard()); } Future<void> _checkAndShowCard() async { // 页面已经销毁就终止流程,避免崩溃 if (!mounted) return; final storage = await SharedPreferences.getInstance(); final lastShowTs = storage.getInt(_lastShowCardKey) ?? 0; final nowTs = DateTime.now().millisecondsSinceEpoch; // 距离上次展示没满7天,直接不弹 if (nowTs - lastShowTs < _weekInMs) return; // 满足条件,弹底部卡片 showModalBottomSheet( context: context, builder: (context) { // 替换为你自己的卡片组件 return SizedBox( height: 220, child: Center(child: Text('每周专属卡片内容')), ); }, ); // 弹完立刻更新本地时间,标记本周已经展示过 // 如果要求用户必须看完/关闭卡片才算展示,把这行逻辑移到卡片关闭的回调里就行 await storage.setInt(_lastShowCardKey, nowTs); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('主页')), body: const Center(child: Text('主页内容区域')), ); } }
额外优化点
- 如果需要支持App挂后台满7天、切回来也能触发卡片,可以给State混入
WidgetsBindingObserver,监听App生命周期变化,在App回到前台的时候再调用一次_checkAndShowCard即可,不用等用户重启App。 - 不要用任何长延时任务做跨天/跨周的触发逻辑,只要进程被杀,内存里的所有任务都会消失,这类需求一律用*“持久化时间点+启动/前台事件触发判断”*的方案实现。
- 弹卡片前一定要加
mounted判断,避免异步任务执行完页面已经销毁,调用context触发崩溃。
内容的提问来源于stack exchange,提问作者user17838882
相关产品推荐
相关产品推荐

