You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 01:34:57