在Flutter应用开发中,本地消息通知是提升用户体验与用户留存率的核心功能之一。通过flutter_local_notifications插件,开发者能够实现跨平台的本地通知功能,包括即时通知、定时通知与周期性通知。本文将深入解析该插件的核心实现逻辑,并提供一个完整的、可复用的NotificationHelper工具类,涵盖初始化、权限请求、通知展示与调度等关键环节。实现方案主要基于flutter_local_notifications: ^18.0.1版本,并整合了permission_handler用于权限管理以及timezone用于处理时区相关的定时任务 。

一、插件集成与初始化

首先,在pubspec.yaml文件中添加必要的依赖。

dependencies:
  flutter_local_notifications: ^18.0.1
  permission_handler: ^11.0.0 # 用于动态权限请求
  timezone: ^0.9.0 # 用于处理时区,支持定时通知

初始化是整个通知功能的基础。以下是一个采用工厂模式封装的NotificationHelper类,其构造函数确保了单例模式,并自动执行初始化。

class NotificationHelper {
  // 单例实例
  static NotificationHelper? _instance = null;

  static NotificationHelper getInstance() {
    _instance ??= NotificationHelper._initial();
    return _instance!;
  }

  factory NotificationHelper() => _instance ??= NotificationHelper._initial();

  // 命名构造函数
  NotificationHelper._initial() {
    initialize();
  }

  final FlutterLocalNotificationsPlugin _notificationsPlugin =
      FlutterLocalNotificationsPlugin();

  // 初始化通知插件
  Future<void> initialize() async {
    try {
      final AndroidInitializationSettings initializationSettingsAndroid =
          AndroidInitializationSettings('@mipmap/ic_launcher'); // Android小图标
      final DarwinInitializationSettings initializationSettingsIOS =
          DarwinInitializationSettings(); // iOS初始化设置
      final InitializationSettings initializationSettings =
          InitializationSettings(
              android: initializationSettingsAndroid,
              iOS: initializationSettingsIOS);
      await _notificationsPlugin.initialize(initializationSettings);
    } catch (e) {
      print('初始化通知插件失败: $e');
    }
  }
}

二、平台权限配置与动态请求

通知功能必须获得操作系统的明确授权,Android与iOS平台的配置方式存在显著差异。

Android平台配置
android/app/src/main/AndroidManifest.xml文件中添加以下权限声明:

<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

其中,SCHEDULE_EXACT_ALARM权限对于在Android 12及以上版本中安排精确时间的定时通知至关重要 。

iOS平台配置
ios/Runner/Info.plist文件中添加权限请求的描述文本,这些文本将显示在系统弹出的权限申请对话框中:

<key>NSUserNotificationAlertIdentifier</key>
<string>我们需要您的许可来发送通知</string>
<key>NSUserNotificationAlertTitle</key>
<string>请求通知权限</string>
<key>NSUserNotificationAlertBody</key>
<string>我们希望能够在您允许的情况下发送通知。</string>

运行时权限请求
静态配置完成后,需在应用运行时动态请求权限。以下方法封装了权限检查与请求逻辑:

Future<void> requestNotificationPermissions() async {
  if (await Permission.notification.isDenied) {
    final status = await Permission.notification.request();
    final status1 = await Permission.scheduleExactAlarm.request();
    if (status.isGranted) {
      print('通知权限已授予');
    } else {
      print('通知权限被拒绝');
    }
  } else {
    print('通知权限已授予');
  }
}

该方法首先检查通知权限是否被拒绝,若已拒绝则同时请求notificationscheduleExactAlarm权限,并记录授权结果 。

三、通知的创建与发送

通知的发送需要分别配置Android与iOS的平台特定参数,然后通过show方法触发。

1. 即时通知
以下方法展示了如何构建并发送一个即时通知。

Future<void> showNotification({required String title, required String body}) async {
  try {
    // Android平台通知详情
    final AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(
            'your.channel.id', // 通知通道ID,需唯一
            'your channel name', // 通道名称
            channelDescription: 'your channel description', // 通道描述
            importance: Importance.max,
            priority: Priority.high,
            ticker: 'ticker' // 状态栏短暂提示文本
        );

    // iOS平台通知详情
    final DarwinNotificationDetails iosNotificationDetails =
        DarwinNotificationDetails(
            categoryIdentifier: 'plainCategory' // 通知分类标识符
        );

    // 合并为跨平台配置
    final NotificationDetails platformChannelSpecifics = NotificationDetails(
        android: androidNotificationDetails, iOS: iosNotificationDetails);

    // 显示通知
    await _notificationsPlugin.show(
      1, // 通知ID,用于后续更新或取消
      title,
      body,
      platformChannelSpecifics,
    );
  } catch (e) {
    print('显示通知失败: $e');
  }
}

2. 定时通知
定时通知的实现依赖于timezone插件来处理时区问题,确保通知在用户本地时间的指定时刻触发。

Future<void> zonedScheduleNotification({
  required int id,
  required String title,
  required String body,
  required DateTime scheduledDateTime
}) async {
  const AndroidNotificationDetails androidNotificationDetails =
      AndroidNotificationDetails('10001', '唤醒',
          channelDescription: 'your channel description',
          importance: Importance.max,
          priority: Priority.high,
          ticker: 'ticker');

  const DarwinNotificationDetails iosNotificationDetails =
      DarwinNotificationDetails(categoryIdentifier: 'plainCategory');

  const NotificationDetails platformChannelSpecifics = NotificationDetails(
      android: androidNotificationDetails, iOS: iosNotificationDetails);

  // 获取本地时区并创建TZDateTime对象
  final location = tz.getLocation(tz.local.name);
  await _notificationsPlugin.zonedSchedule(
    id,
    title,
    body,
    tz.TZDateTime.from(scheduledDateTime, location),
    platformChannelSpecifics,
    uiLocalNotificationDateInterpretation:
        UILocalNotificationDateInterpretation.wallClockTime,
  );
}

3. 周期性通知
周期性通知适用于需要重复提醒的场景,如每日签到。

Future<void> scheduleNotification({
  required int id,
  required String title,
  required String body,
}) async {
  const AndroidNotificationDetails androidNotificationDetails =
      AndroidNotificationDetails('your.channel.id', 'your channel name',
          channelDescription: 'your channel description',
          importance: Importance.max,
          priority: Priority.high,
          ticker: 'ticker');

  const DarwinNotificationDetails iosNotificationDetails =
      DarwinNotificationDetails(categoryIdentifier: 'plainCategory');

  const NotificationDetails platformChannelSpecifics = NotificationDetails(
      android: androidNotificationDetails, iOS: iosNotificationDetails);

  // 每分钟触发一次
  await _notificationsPlugin.periodicallyShow(
      id, title, body, RepeatInterval.everyMinute, platformChannelSpecifics);
}

四、通知的管理与取消

完整的通知功能需要包含管理能力,例如取消特定通知或全部通知。

/// 取消全部通知
Future<void> cancelAll() async {
  await _notificationsPlugin.cancelAll();
}

/// 取消指定ID的通知
Future<void> cancelId(int id) async {
  await _notificationsPlugin.cancel(id);
}

五、核心使用流程总结

将上述功能整合后,在Flutter项目中使用本地通知的标准流程如下:

  1. 权限请求:在应用启动或进入相关功能页时,调用NotificationHelper.getInstance().requestNotificationPermissions();请求用户授权。
  2. 插件初始化:在main函数或首页初始化时,调用NotificationHelper.getInstance().initialize();
  3. 发送通知:根据业务需求,调用showNotificationzonedScheduleNotificationscheduleNotification方法。
  4. 示例调用
    // 安排一个15分钟后触发的定时通知
    NotificationHelper.getInstance().zonedScheduleNotification(
      id: 10001,
      title: "科学研究",
      body: "研究开始了",
      scheduledDateTime: DateTime.now().add(Duration(minutes: 15))
    );
    

六、平台实现要点对比

功能模块 Android实现要点 iOS实现要点
权限声明 AndroidManifest.xml中声明SCHEDULE_EXACT_ALARM权限。 Info.plist中配置权限请求描述文本。
通知通道 必须通过AndroidNotificationDetails创建通知通道(Android 8.0+)。 无需通道概念,通过DarwinNotificationDetails配置分类。
定时通知 依赖SCHEDULE_EXACT_ALARM权限和zonedSchedule方法。 使用zonedSchedule,系统自动处理权限与调度。
图标与样式 通过AndroidInitializationSettings设置默认图标。 图标由Xcode工程中的App Icon配置决定。

通过上述完整的工具类与流程解析,开发者可以快速、稳健地在Flutter应用中集成本地通知功能,有效满足提醒、闹钟、后台任务完成提示等多种业务场景的需求 。


参考来源

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐