如何在Flutter中实现按日期重复的集市日通知功能
解决方案建议
一、核心:集市日计算逻辑
首先需要确定一个基准集市日(首次打开APP时可让用户选择最近的一次集市日,或者预设小镇的固定起始日),之后每3天循环推算。核心代码逻辑如下:
// 计算下一个(或当前)集市日 DateTime calculateNextMarketDay(DateTime baseMarketDay, DateTime currentDate) { DateTime nextMarket = baseMarketDay; // 若基准日在当前日期之前,循环加3天直到找到最近的未来/当天日期 while (nextMarket.isBefore(currentDate)) { nextMarket = nextMarket.add(const Duration(days: 3)); } return nextMarket; }
把基准日存在本地避免重复设置,用SharedPreferences实现:
// 保存基准日 void saveBaseMarketDay(DateTime date) async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setInt('base_market_day', date.millisecondsSinceEpoch); } // 获取基准日 Future<DateTime?> getBaseMarketDay() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int? timestamp = prefs.getInt('base_market_day'); return timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp) : null; }
二、本地通知实现
用flutter_local_notifications库完成通知功能,步骤如下:
- 初始化通知插件
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // 初始化配置 Future<void> initNotifications() async { const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initSettings = InitializationSettings(android: androidSettings); await flutterLocalNotificationsPlugin.initialize(initSettings); }
- 请求通知权限
用户勾选checkbox时先申请权限:
Future<bool> requestNotificationPermission() async { if (Platform.isAndroid) { AndroidFlutterLocalNotificationsPlugin androidPlugin = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()!; return await androidPlugin.requestNotificationsPermission(); } // iOS 权限请求逻辑可按需添加 return true; }
- 设置定时通知
开启通知后,计算触发时间(比如集市日当天早8点)并设置定时任务:
Future<void> scheduleMarketNotification(DateTime marketDay) async { // 设定通知触发时间为集市日当天8:00 DateTime notifyTime = DateTime( marketDay.year, marketDay.month, marketDay.day, 8, 0, ); const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'market_notify_channel', '集市日提醒', channelDescription: '提醒你即将到来的集市日', importance: Importance.high, priority: Priority.high, ); const NotificationDetails notifyDetails = NotificationDetails(android: androidDetails); await flutterLocalNotificationsPlugin.schedule( 0, // 唯一通知ID '集市日提醒', '今天是小镇集市日,别错过啦!', notifyTime, notifyDetails, androidAllowWhileIdle: true, // 后台也能触发通知 ); }
- 处理通知开关状态
用SharedPreferences保存用户的通知开关状态,切换时同步开启/取消通知:
// 保存通知开关状态 void saveNotificationEnabled(bool enabled) async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool('notification_enabled', enabled); } // 获取通知开关状态 Future<bool> getNotificationEnabled() async { SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getBool('notification_enabled') ?? false; } // 切换通知逻辑 void toggleNotification(bool value, DateTime nextMarketDay) async { await saveNotificationEnabled(value); if (value) { bool granted = await requestNotificationPermission(); if (granted) await scheduleMarketNotification(nextMarketDay); } else { await flutterLocalNotificationsPlugin.cancel(0); // 取消已设置的通知 } }
三、UI与逻辑结合
页面初始化时加载数据并更新UI:
@override void initState() { super.initState(); _initPageData(); } Future<void> _initPageData() async { DateTime? baseDay = await getBaseMarketDay(); if (baseDay == null) { // 无基准日时引导用户选择,或默认当前日期 baseDay = DateTime.now(); await saveBaseMarketDay(baseDay); } DateTime nextMarket = calculateNextMarketDay(baseDay, DateTime.now()); bool notifyEnabled = await getNotificationEnabled(); setState(() { _nextMarketDay = nextMarket; _notificationEnabled = notifyEnabled; }); }
最后在checkbox的onChanged回调中调用toggleNotification方法即可。
内容的提问来源于stack exchange,提问作者thebigbrownBox
相关产品推荐
相关产品推荐

