Flutter结合SQLite实现闹钟通知的方法及相关可用包咨询
如何在Flutter中结合SQLite实现闹钟通知并自定义通知声音
一、依赖配置
在pubspec.yaml中添加所需依赖:
dependencies: flutter: sdk: flutter sqflite: ^2.3.2 path_provider: ^2.1.2 flutter_local_notifications: ^16.1.0 android_alarm_manager_plus: ^4.0.4 # 用于后台定时触发(可选,增强可靠性)
二、用SQLite存储闹钟数据
创建数据库助手类,管理闹钟的增删改查,核心存储闹钟的时间、唯一ID、自定义声音路径等信息:
import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; class AlarmDatabaseHelper { static final AlarmDatabaseHelper instance = AlarmDatabaseHelper._init(); static Database? _database; AlarmDatabaseHelper._init(); Future<Database> get database async { if (_database != null) return _database!; _database = await _initDB('alarms.db'); return _database!; } Future<Database> _initDB(String filePath) async { final dbPath = await getDatabasesPath(); final path = join(dbPath, filePath); return await openDatabase(path, version: 1, onCreate: _createDB); } Future _createDB(Database db, int version) async { const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; const textType = 'TEXT NOT NULL'; const integerType = 'INTEGER NOT NULL'; await db.execute(''' CREATE TABLE alarms ( $idType, alarm_time $integerType, sound_path $textType, is_enabled $integerType ) '''); } // 添加闹钟 Future<int> addAlarm(Map<String, dynamic> alarm) async { final db = await instance.database; return await db.insert('alarms', alarm); } // 获取所有闹钟 Future<List<Map<String, dynamic>>> getAlarms() async { final db = await instance.database; return await db.query('alarms'); } // 更新闹钟状态 Future<int> updateAlarm(int id, Map<String, dynamic> updatedAlarm) async { final db = await instance.database; return await db.update( 'alarms', updatedAlarm, where: 'id = ?', whereArgs: [id], ); } // 删除闹钟 Future<int> deleteAlarm(int id) async { final db = await instance.database; return await db.delete( 'alarms', where: 'id = ?', whereArgs: [id], ); } }
三、替代AlarmManager的可靠闹钟调度方案
如果原生AlarmManager效果不佳,推荐使用flutter_local_notifications直接调度定时通知,它在Android和iOS上兼容性更好,能有效处理后台唤醒问题:
1. 初始化通知服务
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:timezone/data/latest_all.dart' as tz; import 'package:timezone/timezone.dart' as tz; FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); Future<void> initNotifications() async { tz.initializeTimeZones(); final location = tz.getLocation('Asia/Shanghai'); // 根据你的时区调整 tz.setLocalLocation(location); // Android初始化设置 const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); // iOS初始化设置 const DarwinInitializationSettings initializationSettingsIOS = DarwinInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); const InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, ); await flutterLocalNotificationsPlugin.initialize(initializationSettings); // Android 8.0+ 创建通知渠道(必须) const AndroidNotificationChannel channel = AndroidNotificationChannel( 'alarm_channel', '闹钟通知', description: '用于触发闹钟提醒', importance: Importance.max, sound: RawResourceAndroidNotificationSound('custom_alarm'), // 默认自定义声音 ); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); }
2. 从SQLite读取数据并设置定时闹钟
Future<void> scheduleAlarmFromDB() async { final alarms = await AlarmDatabaseHelper.instance.getAlarms(); for (var alarm in alarms) { if (alarm['is_enabled'] == 1) { final alarmTime = DateTime.fromMillisecondsSinceEpoch(alarm['alarm_time']); final soundPath = alarm['sound_path']; // 构建通知详情 final AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'alarm_channel', '闹钟通知', sound: soundPath.isNotEmpty ? UriAndroidNotificationSound(soundPath) : const RawResourceAndroidNotificationSound('custom_alarm'), importance: Importance.max, priority: Priority.high, enableVibration: true, ); const DarwinNotificationDetails iosDetails = DarwinNotificationDetails( sound: 'custom_alarm.wav', // iOS需确保文件在Resources目录 ); final NotificationDetails notificationDetails = NotificationDetails( android: androidDetails, iOS: iosDetails, ); // 调度定时通知 await flutterLocalNotificationsPlugin.zonedSchedule( alarm['id'], '闹钟提醒', '设定的时间到了', tz.TZDateTime.from(alarmTime, tz.local), notificationDetails, androidAllowWhileIdle: true, // 允许Doze模式下触发 uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, matchDateTimeComponents: DateTimeComponents.time, // 每天重复(可选) ); } } }
四、自定义通知声音的实现
1. 文件放置要求
- Android:将音频文件(如
custom_alarm.mp3)放到android/app/src/main/res/raw目录(没有则创建) - iOS:将音频文件(如
custom_alarm.wav)添加到项目的Resources目录,并在Info.plist中添加UILocalNotificationUsageDescription权限
2. 动态设置自定义声音
如果允许用户选择本地音频文件,可通过path_provider获取文件路径,再在通知详情中指定:
// 假设已获取用户选择的音频文件路径 final String selectedSoundPath = '/storage/emulated/0/Download/custom_sound.mp3'; final AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'alarm_channel', '闹钟通知', sound: UriAndroidNotificationSound(selectedSoundPath), // 其他配置... );
关键注意事项
- Android权限:在
AndroidManifest.xml中添加必要权限:<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.USE_EXACT_ALARM" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> - 后台限制:Android的Doze模式会限制后台任务,需启用
androidAllowWhileIdle: true并申请精确闹钟权限 - iOS后台配置:在
Info.plist中开启UIBackgroundModes的remote-notification和fetch选项
内容的提问来源于stack exchange,提问作者Vathsala Thomas
相关产品推荐
相关产品推荐

