Flutter iOS课程提醒通知实现求助:flutter_local_notifications未生效
Flutter iOS课程表应用课前5分钟通知实现方案
你的代码已完成课程倒计时和日程展示功能,但未实现flutter_local_notifications的初始化、权限申请及通知调度逻辑,这是通知无法工作的核心原因。以下是适配iOS平台的完整解决方案:
步骤1:配置依赖与iOS权限
- 在
pubspec.yaml中确保依赖最新稳定版插件:
dependencies: flutter: sdk: flutter flutter_local_notifications: ^16.1.0 # 请使用最新版本
- 在iOS项目的
Info.plist中添加通知权限描述:
<key>NSNotificationAlertUsageDescription</key> <string>课程开始前5分钟推送提醒</string> <key>NSNotificationSoundUsageDescription</key> <string>通知提醒声音</string> <key>NSNotificationBadgeUsageDescription</key> <string>通知角标显示</string>
步骤2:完整代码实现
以下是整合通知功能的完整代码,关键部分已添加注释:
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; void main() { runApp(const MaterialApp(home: MyApp())); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { late Timer _timer; String _timeRemaining = ''; // 通知插件实例 late FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin; // 记录已发送的通知ID,避免重复推送 Set<int> _sentNotificationIds = {}; @override void initState() { super.initState(); _initNotifications(); _startTimer(); } @override void dispose() { _timer.cancel(); super.dispose(); } // 初始化通知插件并请求iOS权限 void _initNotifications() { _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const DarwinInitializationSettings initializationSettingsIOS = DarwinInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); const InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, ); _flutterLocalNotificationsPlugin.initialize(initializationSettings); } void _startTimer() { _timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { _timeRemaining = _getTimeRemaining(); }); // 每次计时检查是否需要发送通知 _checkAndSendNotification(); }); } // 检查是否到达课程前5分钟,发送通知 void _checkAndSendNotification() { DateTime now = DateTime.now(); DateTime nextLesson = _getNextLessonTime(); Duration difference = nextLesson.difference(now); // 当剩余时间在4分50秒到5分10秒之间,且未发送过该课程的通知 if (difference.inSeconds >= 290 && difference.inSeconds <= 310) { int notificationId = nextLesson.millisecondsSinceEpoch ~/ 1000; if (!_sentNotificationIds.contains(notificationId)) { _sendNotification(notificationId, _getNextLessonName(nextLesson)); _sentNotificationIds.add(notificationId); } } // 课程结束后移除已发送的通知ID,避免影响次日同课程的提醒 if (difference.isNegative) { _sentNotificationIds.clear(); } } // 发送通知 Future<void> _sendNotification(int id, String lessonName) async { const AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails( 'lesson_reminder_channel', '课程提醒', channelDescription: '课程开始前5分钟推送提醒', importance: Importance.high, priority: Priority.high, ); const DarwinNotificationDetails iosNotificationDetails = DarwinNotificationDetails(); const NotificationDetails notificationDetails = NotificationDetails( android: androidNotificationDetails, iOS: iosNotificationDetails, ); await _flutterLocalNotificationsPlugin.show( id, '课程即将开始', '$lessonName 还有5分钟开始,请做好准备!', notificationDetails, ); } String _getTimeRemaining() { DateTime now = DateTime.now(); DateTime tomorrow = DateTime(now.year, now.month, now.day + 1); DateTime nextLesson = _getNextLessonTime(); Duration difference = nextLesson.difference(now); if (difference.isNegative) { nextLesson = _getNextLessonTime(); difference = nextLesson.difference(now); } int seconds = difference.inSeconds % 60; int minutes = (difference.inMinutes % 60); int hours = difference.inHours % 24; String nextLessonName = _getNextLessonName(nextLesson); String timeRemaining; if (now.weekday == DateTime.sunday) { timeRemaining = '今天不上课。'; } else if (tomorrow.weekday == DateTime.sunday) { timeRemaining = '明天不上课。'; } else if (now.weekday == DateTime.saturday && now.hour >= 12) { timeRemaining = '今日课程已结束。'; } else if (now.hour >= 14) { timeRemaining = '今日课程已结束。'; } else if (difference.inHours >= 24) { timeRemaining = '下节课\n $nextLessonName 将于 ${nextLesson.day}/${nextLesson.month}/${nextLesson.year},还有 ${difference.inDays} 天。'; } else if (now.hour >= nextLesson.hour && now.minute >= nextLesson.minute) { nextLesson = _getNextLessonTime(); difference = nextLesson.difference(now); seconds = difference.inSeconds % 60; minutes = (difference.inMinutes % 60); hours = difference.inHours % 24; nextLessonName = _getNextLessonName(nextLesson); timeRemaining = '下节课\n $nextLessonName 还有 ${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}。'; } else { timeRemaining = '下节课\n $nextLessonName 还有 ${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}。'; } return timeRemaining; } DateTime _getNextLessonTime() { DateTime now = DateTime.now(); DateTime nextLesson; if (now.weekday == DateTime.saturday) { nextLesson = DateTime(now.year, now.month, now.day, 10, 0); // 如果周六10点已过,返回下周一8点 if (now.isAfter(nextLesson)) { nextLesson = nextLesson.add(const Duration(days: 2)); } } else { nextLesson = DateTime(now.year, now.month, now.day, 8, 0); if (now.hour >= 14) { nextLesson = nextLesson.add(const Duration(days: 1)); // 如果次日是周日,跳转到周一 if (nextLesson.weekday == DateTime.sunday) { nextLesson = nextLesson.add(const Duration(days: 1)); } } else { // 当天未到14点,但8点已过,获取下一节课程的时间 nextLesson = _getNextLessonOfDay(now); } } return nextLesson; } // 获取当天的下一节课程时间(原逻辑仅返回8点,这里补充完整课程时间判断) DateTime _getNextLessonOfDay(DateTime now) { List<DateTime> todayLessons = []; switch (now.weekday) { case DateTime.monday: todayLessons = [ DateTime(now.year, now.month, now.day, 8, 0), DateTime(now.year, now.month, now.day, 9, 0), DateTime(now.year, now.month, now.day, 10, 0), DateTime(now.year, now.month, now.day, 12, 0), ]; break; case DateTime.tuesday: todayLessons = [ DateTime(now.year, now.month, now.day, 8, 0), DateTime(now.year, now.month, now.day, 9, 0), DateTime(now.year, now.month, now.day, 10, 0), DateTime(now.year, now.month, now.day, 11, 0), DateTime(now.year, now.month, now.day, 12, 0), DateTime(now.year, now.month, now.day, 13, 0), ]; break; case DateTime.wednesday: todayLessons = [ DateTime(now.year, now.month, now.day, 8, 0), DateTime(now.year, now.month, now.day, 9, 0), DateTime(now.year, now.month, now.day, 10, 0), DateTime(now.year, now.month, now.day, 11, 0), DateTime(now.year, now.month, now.day, 12, 0), DateTime(now.year, now.month, now.day, 13, 0), ]; break; case DateTime.thursday: todayLessons = [ DateTime(now.year, now.month, now.day, 8, 0), DateTime(now.year, now.month, now.day, 9, 0), DateTime(now.year, now.month, now.day, 10, 0), DateTime(now.year, now.month, now.day, 11, 0), DateTime(now.year, now.month, now.day, 12, 0), DateTime(now.year, now.month, now.day, 13, 0), ]; break; case DateTime.friday: todayLessons = [ DateTime(now.year, now.month, now.day, 8, 0), DateTime(now.year, now.month, now.day, 9, 0), DateTime(now.year, now.month, now.day, 10, 0), DateTime(now.year, now.month, now.day, 11, 0), ]; break; default: todayLessons = [DateTime(now.year, now.month, now.day, 8, 0)]; } // 找到第一个未过去的课程时间 for (var lessonTime in todayLessons) { if (now.isBefore(lessonTime)) { return lessonTime; } } // 所有课程已过,返回次日8点(已处理周日情况) DateTime nextDay = now.add(const Duration(days: 1)); if (nextDay.weekday == DateTime.sunday) { nextDay = nextDay.add(const Duration(days: 1)); } return DateTime(nextDay.year, nextDay.month, nextDay.day, 8, 0); } String _getNextLessonName(DateTime nextLesson) { String nextLessonName = ''; if (nextLesson.weekday == DateTime.monday) { // 根据课程时间匹配课程名称 if (nextLesson.hour == 8) { nextLessonName = 'Telecomunicazioni'; } else if (nextLesson.hour == 9) { nextLessonName = 'Matematica'; } else if (nextLesson.hour == 10) { nextLessonName = 'Italiano'; } else if (nextLesson.hour == 12) { nextLessonName = 'Inglese'; } } else if (nextLesson.weekday == DateTime.tuesday) { if (nextLesson.hour == 8) { nextLessonName = 'Italiano'; } else if (nextLesson.hour >=9 && nextLesson.hour <=10) { nextLessonName = 'Sistemi'; } else if (nextLesson.hour >=11 && nextLesson.hour <=13) { nextLessonName = 'Informatica'; } } else if (nextLesson.weekday == DateTime.wednesday) { if (nextLesson.hour == 8) { nextLessonName = 'Italiano'; } else if (nextLesson.hour ==9) { nextLessonName = 'Storia'; } else if (nextLesson.hour ==10) { nextLessonName = 'Informatica'; } else if (nextLesson.hour ==11) { nextLessonName = 'Educazione Fisica'; } else if (nextLesson.hour ==12) { nextLessonName = 'Matematica'; } else if (nextLesson.hour ==13) { nextLessonName = 'Inglese'; } } else if (nextLesson.weekday == DateTime.thursday) { if (nextLesson.hour ==8) { nextLessonName = 'Sistemi'; } else if (nextLesson.hour ==9) { nextLessonName = 'Matematica'; } else if (nextLesson.hour ==10) { nextLessonName = 'Storia'; } else if (nextLesson.hour ==11) { nextLessonName = 'Informatica'; } else if (nextLesson.hour ==12) { nextLessonName = 'Inglese'; } else if (nextLesson.hour ==13) { nextLessonName = 'T.P.I.'; } } else if (nextLesson.weekday == DateTime.friday) { if (nextLesson.hour ==8) { nextLessonName = 'Matematica'; } else if (nextLesson.hour ==9) { nextLessonName = 'Sistemi'; } else if (nextLesson.hour ==10) { nextLessonName = 'Informatica'; } else if (nextLesson.hour ==11) { nextLessonName = 'Educazione Fisica'; } } else if (nextLesson.weekday == DateTime.saturday) { if (nextLesson.hour >=8 && nextLesson.hour <=9) { nextLessonName = 'Telecomunicazioni'; } else if (nextLesson.hour >=10 && nextLesson.hour <=11) { nextLessonName = 'T.P.I.'; } } return nextLessonName; } @override Widget build(BuildContext context) { return MaterialApp( title: '课程表', home: Scaffold( appBar: AppBar( title: const Text('课程表'), centerTitle: true, ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Center( child: Text( _timeRemaining, style: const TextStyle(fontSize: 20), textAlign: TextAlign.center, ), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ _buildDayButton('周一', context), _buildDayButton('周二', context), ], ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ _buildDayButton('周三', context), _buildDayButton('周四', context), ], ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ _buildDayButton('周五', context), _buildDayButton('周六', context), ], ), const SizedBox(height: 20), const Text( '程序由Skerdi Velo开发', style: TextStyle(fontSize: 12), ), ], ), ), ), ); } Widget _buildDayButton(String day, BuildContext context) { String schedule = ''; switch (day) { case '周一': schedule = '08:00-09:00 Telecomunicazioni\n09:00-10:00 Matematica\n10:00-12:00 Italiano\n12:00-13:
相关产品推荐
相关产品推荐

