Flutter for OpenHarmony三方库适配实战:flutter_local_notifications 本地通知

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
本文基于flutter3.27.5开发

一、flutter_local_notifications 库概述

本地通知是移动应用与用户交互的重要方式,用于提醒用户、推送消息、定时提醒等场景。在 Flutter for OpenHarmony 应用开发中,flutter_local_notifications 是一个功能丰富的本地通知插件,提供了完整的跨平台通知能力。

flutter_local_notifications 库特点

flutter_local_notifications 库基于 Flutter 平台接口实现,提供了以下核心特性:

即时通知:支持立即显示本地通知,可自定义标题、内容、图标等。

定时通知:支持基于时间、日期的定时通知,以及周期性重复通知。

通知样式:支持多种通知样式,包括大文本、大图片、收件箱样式等。

通知渠道:支持通知渠道管理,可设置重要性、声音、振动等属性。

通知操作:支持通知点击回调、通知操作按钮等交互功能。

通知管理:支持取消通知、获取活动通知、获取待发送通知等功能。

功能支持对比

功能 Android iOS OpenHarmony
即时通知
定时通知
周期性通知
通知样式
通知渠道
通知操作
点击回调
取消通知

使用场景:消息提醒、定时闹钟、任务提醒、下载完成通知、日历提醒等。


二、安装与配置

2.1 添加依赖

在项目的 pubspec.yaml 文件中添加 flutter_local_notifications 依赖:

yaml 复制代码
dependencies:
  flutter_local_notifications:
    git:
      url: https://atomgit.com/openharmony-sig/fluttertpc_flutter_local_notifications.git
      path: flutter_local_notifications

然后执行以下命令获取依赖:

bash 复制代码
flutter pub get

2.2 权限配置

flutter_local_notifications 在 OpenHarmony 平台上需要配置不同的权限:

  • 即时通知:需要在运行时请求通知权限
  • 定时通知 :需要在 module.json5 中配置 ohos.permission.PUBLISH_AGENT_REMINDER 权限

重要:OpenHarmony 的通知权限需要在应用运行时动态请求,用户授权后才能发送通知。如果不请求权限,通知将无法正常显示。

2.2.1 权限请求方法

在初始化通知插件后,需要调用 requestNotificationsPermission() 方法请求权限:

dart 复制代码
// 获取 OpenHarmony 平台特定的通知插件实例
final OhosFlutterLocalNotificationsPlugin? ohosImplementation = 
    flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
        OhosFlutterLocalNotificationsPlugin>();

// 请求通知权限
final bool? granted = await ohosImplementation?.requestNotificationsPermission();
if (granted ?? false) {
  print('通知权限已授权');
} else {
  print('通知权限被拒绝');
}
2.2.2 检查权限状态

可以使用 areNotificationsEnabled() 方法检查通知权限是否已启用:

dart 复制代码
final bool enabled = await flutterLocalNotificationsPlugin
    .resolvePlatformSpecificImplementation<
        OhosFlutterLocalNotificationsPlugin>()
    ?.areNotificationsEnabled() ?? false;

if (enabled) {
  print('通知权限已启用');
} else {
  print('通知权限未启用,需要请求权限');
}
2.2.3 定时通知权限配置

重要 :定时通知(zonedScheduleperiodicallyShow)需要额外的权限配置。

步骤 1:在 module.json5 中添加权限

ohos/entry/src/main/module.json5 文件的 requestPermissions 数组中添加:

json 复制代码
{
  "name": "ohos.permission.PUBLISH_AGENT_REMINDER",
  "reason": "$string:reminder_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "always"
  }
}

步骤 2:添加权限说明字符串

ohos/entry/src/main/resources/base/element/string.json 中添加:

json 复制代码
{
  "name": "reminder_reason",
  "value": "需要发送定时提醒通知"
}

步骤 3:真机代理提醒权限申请(重要!)

警告:OpenHarmony 真机对代理提醒功能有管控机制。未通过管控的应用无法使用后台代理提醒能力,即使配置了权限,定时通知也无法正常工作。

真机定时通知不工作的原因:

  • OpenHarmony 为防止滥用后台代理提醒能力(如发送广告、营销类提醒),增加了管控机制
  • 未通过管控的应用无法使用代理提醒功能
  • 症状:定时通知设置成功,但 pendingNotificationRequests() 返回空列表,通知不会触发

注意事项:

  • 当前仅对纯工具类应用开放申请
  • 申请审批后才能正常使用代理提醒功能
  • 模拟器上可能不受此限制

注意 :如果不配置 ohos.permission.PUBLISH_AGENT_REMINDER 权限,定时通知将无法正常工作,pendingNotificationRequests() 方法也会返回空列表。


三、核心 API 详解

3.1 FlutterLocalNotificationsPlugin 类

FlutterLocalNotificationsPlugin 是核心类,提供所有通知操作方法。

dart 复制代码
class FlutterLocalNotificationsPlugin {
  factory FlutterLocalNotificationsPlugin();
  
  Future<bool?> initialize(InitializationSettings initializationSettings, {...});
  Future<void> show(int id, String? title, String? body, NotificationDetails? notificationDetails, {...});
  Future<void> zonedSchedule(int id, String? title, String? body, TZDateTime scheduledDate, NotificationDetails notificationDetails, {...});
  Future<void> periodicallyShow(int id, String? title, String? body, RepeatInterval repeatInterval, NotificationDetails notificationDetails, {...});
  Future<void> cancel(int id, {String? tag});
  Future<void> cancelAll();
  // ...
}

3.2 initialize 方法

initialize 方法用于初始化通知插件,必须在应用启动时调用。

dart 复制代码
Future<bool?> initialize(
  InitializationSettings initializationSettings, {
  DidReceiveNotificationResponseCallback? onDidReceiveNotificationResponse,
  DidReceiveBackgroundNotificationResponseCallback? onDidReceiveBackgroundNotificationResponse,
})

参数说明

initializationSettings 参数是初始化设置,包含各平台的配置。

onDidReceiveNotificationResponse 参数是通知点击回调。

使用示例

dart 复制代码
final FlutterLocalNotificationsPlugin notifications = FlutterLocalNotificationsPlugin();

// 初始化时区数据库
tz.initializeTimeZones();

// 初始化通知插件
await notifications.initialize(
  InitializationSettings(
    ohos: OhosInitializationSettings('app_icon'),
  ),
  onDidReceiveNotificationResponse: (NotificationResponse response) {
    print('通知被点击: ${response.payload}');
  },
);

// 设置本地时区(定时通知必需)
final String? timeZoneName = await notifications.getLocalTimezone();
if (timeZoneName != null) {
  tz.setLocalLocation(tz.getLocation(timeZoneName));
  print('本地时区: $timeZoneName');
}

// 请求通知权限(重要!)
final bool? granted = await notifications
    .resolvePlatformSpecificImplementation<OhosFlutterLocalNotificationsPlugin>()
    ?.requestNotificationsPermission();

if (granted ?? false) {
  print('通知权限已授权,可以发送通知');
} else {
  print('通知权限被拒绝,通知将无法正常显示');
}

重要提示

  1. 在 OpenHarmony 平台上,必须在初始化后请求通知权限,否则通知将无法正常显示
  2. 定时通知必须设置本地时区,否则定时通知无法正常工作
  3. 建议在应用启动时检查并请求权限

3.3 show 方法

show 方法用于立即显示通知。

dart 复制代码
Future<void> show(
  int id,
  String? title,
  String? body,
  NotificationDetails? notificationDetails, {
  String? payload,
})

参数说明

id 参数是通知唯一标识,用于后续取消或更新通知。

title 参数是通知标题。

body 参数是通知内容。

notificationDetails 参数是通知详情配置。

payload 参数是自定义数据,点击通知时传递给回调。

使用示例

dart 复制代码
await notifications.show(
  1,
  '通知标题',
  '这是通知内容',
  NotificationDetails(
    ohos: OhosNotificationDetails(
      OhosNotificationSlotType.SOCIAL_COMMUNICATION,
      importance: OhosImportance.high,
    ),
  ),
  payload: 'custom_data',
);

3.4 zonedSchedule 方法

zonedSchedule 方法用于定时发送通知。

dart 复制代码
Future<void> zonedSchedule(
  int id,
  String? title,
  String? body,
  TZDateTime scheduledDate,
  NotificationDetails notificationDetails, {
  String? payload,
  DateTimeComponents? matchDateTimeComponents,
})

参数说明

scheduledDate 参数是通知发送时间,使用 TZDateTime 类型。

matchDateTimeComponents 参数用于设置重复周期,如每天、每周等。

使用示例

dart 复制代码
await notifications.zonedSchedule(
  2,
  '定时通知',
  '这是定时通知内容',
  TZDateTime.now(local).add(Duration(seconds: 10)),
  NotificationDetails(
    ohos: OhosNotificationDetails(
      OhosNotificationSlotType.SOCIAL_COMMUNICATION,
    ),
  ),
  uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
  payload: 'scheduled_notification',
);

3.5 periodicallyShow 方法

periodicallyShow 方法用于周期性发送通知。

dart 复制代码
Future<void> periodicallyShow(
  int id,
  String? title,
  String? body,
  RepeatInterval repeatInterval,
  NotificationDetails notificationDetails, {
  String? payload,
})

参数说明

repeatInterval 参数是重复间隔,支持每分钟、每小时、每天、每周等。

使用示例

dart 复制代码
await notifications.periodicallyShow(
  3,
  '周期通知',
  '这是周期性通知',
  RepeatInterval.hourly,
  NotificationDetails(
    ohos: OhosNotificationDetails(
      OhosNotificationSlotType.SOCIAL_COMMUNICATION,
    ),
  ),
);

3.6 cancel 方法

cancel 方法用于取消指定通知。

dart 复制代码
Future<void> cancel(int id, {String? tag})

参数说明

id 参数是要取消的通知 ID。

tag 参数是通知标签(可选)。

使用示例

dart 复制代码
await notifications.cancel(1);

3.7 cancelAll 方法

cancelAll 方法用于取消所有通知。

dart 复制代码
Future<void> cancelAll()

使用示例

dart 复制代码
await notifications.cancelAll();

3.8 getNotificationAppLaunchDetails 方法

getNotificationAppLaunchDetails 方法用于获取应用是否通过通知启动。

dart 复制代码
Future<NotificationAppLaunchDetails?> getNotificationAppLaunchDetails()

返回值 :返回 NotificationAppLaunchDetails 对象,包含启动信息。

使用示例

dart 复制代码
final details = await notifications.getNotificationAppLaunchDetails();
if (details?.didNotificationLaunchApp ?? false) {
  print('应用通过通知启动');
}

3.9 pendingNotificationRequests 方法

pendingNotificationRequests 方法用于获取待发送的通知列表。

dart 复制代码
Future<List<PendingNotificationRequest>> pendingNotificationRequests()

返回值:返回待发送通知列表。

使用示例

dart 复制代码
final pending = await notifications.pendingNotificationRequests();
for (var request in pending) {
  print('待发送通知: ${request.id}, ${request.title}, ${request.body}');
}

注意 :OpenHarmony API 12 版本暂不支持读取 payload 字段,返回的 PendingNotificationRequest 对象中 payload 字段将为空。


四、数据模型详解

4.1 InitializationSettings 类

InitializationSettings 类包含各平台的初始化设置。

dart 复制代码
class InitializationSettings {
  const InitializationSettings({
    this.android,
    this.iOS,
    this.macOS,
    this.linux,
    this.ohos
  });
  
  final AndroidInitializationSettings? android;
  final DarwinInitializationSettings? iOS;
  final DarwinInitializationSettings? macOS;
  final LinuxInitializationSettings? linux;
  final OhosInitializationSettings? ohos;
}

4.2 OhosInitializationSettings 类

OhosInitializationSettings 类是 OpenHarmony 平台的初始化设置。

dart 复制代码
class OhosInitializationSettings {
  const OhosInitializationSettings(this.defaultIcon);
  
  final String defaultIcon;  // 默认通知图标
}

4.3 NotificationDetails 类

NotificationDetails 类包含各平台的通知详情。

dart 复制代码
class NotificationDetails {
  const NotificationDetails({
    this.android,
    this.iOS,
    this.macOS,
    this.linux,
    this.ohos
  });
  
  final AndroidNotificationDetails? android;
  final DarwinNotificationDetails? iOS;
  final DarwinNotificationDetails? macOS;
  final LinuxNotificationDetails? linux;
  final OhosNotificationDetails? ohos;
}

4.4 OhosNotificationDetails 类

OhosNotificationDetails 类是 OpenHarmony 平台的通知详情。

dart 复制代码
class OhosNotificationDetails {
  const OhosNotificationDetails(
    this.slotType, {
    this.icon,
    this.importance = OhosImportance.defaultImportance,
    this.styleInformation,
    this.playSound = true,
    this.enableVibration = true,
    this.groupKey,
    this.autoCancel = true,
    this.ongoing = false,
    this.silent = false,
    this.color,
    this.largeIcon,
    this.showProgress = false,
    this.maxProgress = 0,
    this.progress = 0,
    // ...
  });
  
  final String? icon;                        // 通知图标
  final OhosNotificationSlotType slotType;   // 通知渠道类型
  final OhosImportance importance;           // 重要性级别
  final OhosStyleInformation? styleInformation; // 通知样式
  final bool playSound;                      // 是否播放声音
  final bool enableVibration;                // 是否振动
  final String? groupKey;                    // 分组键
  final bool autoCancel;                     // 点击后自动取消
  final bool ongoing;                        // 是否持续显示
  final bool showProgress;                   // 显示进度条
  final int maxProgress;                     // 最大进度
  final int progress;                        // 当前进度
}

4.5 OhosNotificationSlotType 枚举

OhosNotificationSlotType 枚举定义了通知渠道类型。

dart 复制代码
enum OhosNotificationSlotType {
  UNKNOWN_TYPE,          // 未知类型
  SOCIAL_COMMUNICATION,  // 社交通信
  SERVICE_INFORMATION,   // 服务信息
  CONTENT_INFORMATION,   // 内容信息
  LIVE_VIEW,            // 直播
  CUSTOMER_SERVICE,     // 客户服务
}

4.6 OhosImportance 枚举

OhosImportance 枚举定义了通知重要性级别。

dart 复制代码
enum OhosImportance {
  none,              // 不显示
  min,               // 最低
  low,               // 低
  defaultImportance, // 默认
  high,              // 高
}

4.7 RepeatInterval 枚举

RepeatInterval 枚举定义了重复间隔。

dart 复制代码
enum RepeatInterval {
  everyMinute,    // 每分钟
  hourly,         // 每小时
  daily,          // 每天
  weekly,         // 每周
}

五、OpenHarmony 平台实现原理

5.1 原生 API 映射

flutter_local_notifications 在 OpenHarmony 平台上使用 @kit.NotificationKit@kit.BackgroundTasksKit 模块实现:

Flutter API OpenHarmony API
show notificationManager.publish
zonedSchedule reminderAgentManager.publishReminder
periodicallyShow reminderAgentManager.publishReminder
cancel notificationManager.cancel
cancelAll notificationManager.cancelAll

5.2 通知渠道实现

OpenHarmony 使用 NotificationSlot 实现通知渠道管理:

typescript 复制代码
let notificationSlot: notificationManager.NotificationSlot = {
  id: slotId,
  type: slotType,
  level: importance,
  badgeFlag: showBadge,
  sound: sound,
  vibrationEnabled: enableVibration,
};
await notificationManager.addSlot(notificationSlot);

5.3 定时通知实现

OpenHarmony 使用 reminderAgentManager 实现定时通知:

typescript 复制代码
let reminderRequest: reminderAgentManager.ReminderRequestTimer = {
  reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER,
  triggerTimeInSeconds: triggerTime,
  notificationId: notificationId,
  title: title,
  content: body,
};
await reminderAgentManager.publishReminder(reminderRequest);

六、通知样式详解

6.1 默认样式

dart 复制代码
OhosNotificationDetails(
  OhosNotificationSlotType.SOCIAL_COMMUNICATION,
  styleInformation: OhosDefaultStyleInformation(),
);

6.2 大文本样式

dart 复制代码
OhosNotificationDetails(
  OhosNotificationSlotType.SOCIAL_COMMUNICATION,
  styleInformation: OhosBigTextStyleInformation(
    '这是一段很长的通知内容,可以显示更多文字信息...',
    contentTitle: '大文本标题',
    summaryText: '摘要',
  ),
);

6.3 大图片样式

dart 复制代码
OhosNotificationDetails(
  OhosNotificationSlotType.SOCIAL_COMMUNICATION,
  styleInformation: OhosBigPictureStyleInformation(
    OhosFilePath('path/to/image.png'),
    contentTitle: '大图片标题',
    summaryText: '摘要',
  ),
);

6.4 收件箱样式

dart 复制代码
OhosNotificationDetails(
  OhosNotificationSlotType.SOCIAL_COMMUNICATION,
  styleInformation: OhosInboxStyleInformation(
    ['消息1', '消息2', '消息3'],
    contentTitle: '收件箱',
    summaryText: '3条新消息',
  ),
);

七、实战案例

7.1 基础通知

dart 复制代码
Future<void> showBasicNotification() async {
  final notifications = FlutterLocalNotificationsPlugin();
  
  await notifications.show(
    1,
    '基础通知',
    '这是一条基础通知',
    NotificationDetails(
      ohos: OhosNotificationDetails(
        OhosNotificationSlotType.SOCIAL_COMMUNICATION,
        importance: OhosImportance.high,
      ),
    ),
  );
}

7.2 带进度条的通知

dart 复制代码
Future<void> showProgressNotification(int progress) async {
  final notifications = FlutterLocalNotificationsPlugin();
  
  await notifications.show(
    2,
    '下载中',
    '正在下载文件...',
    NotificationDetails(
      ohos: OhosNotificationDetails(
        OhosNotificationSlotType.SERVICE_INFORMATION,
        showProgress: true,
        maxProgress: 100,
        progress: progress,
        ongoing: true,
      ),
    ),
  );
}

7.3 定时通知

dart 复制代码
Future<void> scheduleNotification() async {
  final notifications = FlutterLocalNotificationsPlugin();
  
  final scheduledDate = TZDateTime.now(local).add(Duration(minutes: 5));
  
  await notifications.zonedSchedule(
    3,
    '定时提醒',
    '这是5分钟后的提醒',
    scheduledDate,
    NotificationDetails(
      ohos: OhosNotificationDetails(
        OhosNotificationSlotType.SOCIAL_COMMUNICATION,
      ),
    ),
    uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
    payload: 'scheduled_5min',
  );
}

7.4 每日重复通知

dart 复制代码
Future<void> scheduleDailyNotification() async {
  final notifications = FlutterLocalNotificationsPlugin();
  
  final scheduledDate = TZDateTime(
    local,
    DateTime.now().year,
    DateTime.now().month,
    DateTime.now().day,
    9, 0, 0,  // 每天上午9点
  );
  
  await notifications.zonedSchedule(
    4,
    '每日提醒',
    '早上好!新的一天开始了',
    scheduledDate,
    NotificationDetails(
      ohos: OhosNotificationDetails(
        OhosNotificationSlotType.SOCIAL_COMMUNICATION,
      ),
    ),
    uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
    matchDateTimeComponents: DateTimeComponents.time,
  );
}

7.5 处理通知点击

dart 复制代码
Future<void> initNotifications() async {
  final notifications = FlutterLocalNotificationsPlugin();
  
  await notifications.initialize(
    InitializationSettings(
      ohos: OhosInitializationSettings('app_icon'),
    ),
    onDidReceiveNotificationResponse: (NotificationResponse response) {
      final payload = response.payload;
      final notificationId = response.id;
  
      if (payload != null) {
        print('通知被点击,payload: $payload');
        // 根据payload跳转到对应页面
      }
    },
  );
}

7.6 检查应用启动来源

dart 复制代码
Future<void> checkLaunchSource() async {
  final notifications = FlutterLocalNotificationsPlugin();
  
  final details = await notifications.getNotificationAppLaunchDetails();
  
  if (details?.didNotificationLaunchApp ?? false) {
    final response = details?.notificationResponse;
    print('应用通过通知启动');
    print('通知ID: ${response?.id}');
    print('Payload: ${response?.payload}');
  }
}

八、最佳实践

8.1 初始化时机

在应用启动时尽早初始化通知插件:

dart 复制代码
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  final notifications = FlutterLocalNotificationsPlugin();
  await notifications.initialize(
    InitializationSettings(
      ohos: OhosInitializationSettings('@mipmap/ic_launcher'),
    ),
    onDidReceiveNotificationResponse: handleNotificationResponse,
  );
  
  runApp(MyApp());
}

8.2 通知 ID 管理

使用常量或枚举管理通知 ID:

dart 复制代码
class NotificationIds {
  static const int downloadComplete = 1;
  static const int dailyReminder = 2;
  static const int messageNotification = 3;
}

8.3 通知渠道管理

根据通知类型使用不同的渠道:

dart 复制代码
OhosNotificationDetails getNotificationDetails(String type) {
  switch (type) {
    case 'message':
      return OhosNotificationDetails(
        OhosNotificationSlotType.SOCIAL_COMMUNICATION,
        importance: OhosImportance.high,
        playSound: true,
      );
    case 'download':
      return OhosNotificationDetails(
        OhosNotificationSlotType.SERVICE_INFORMATION,
        importance: OhosImportance.low,
        playSound: false,
      );
    default:
      return OhosNotificationDetails(
        OhosNotificationSlotType.CONTENT_INFORMATION,
      );
  }
}

8.4 及时取消通知

不再需要的通知应及时取消:

dart 复制代码
Future<void> cleanupNotifications() async {
  final notifications = FlutterLocalNotificationsPlugin();
  
  // 取消特定通知
  await notifications.cancel(NotificationIds.downloadComplete);
  
  // 或取消所有通知
  await notifications.cancelAll();
}

8.5 错误处理

dart 复制代码
Future<void> safeShowNotification() async {
  try {
    await notifications.show(
      1,
      '标题',
      '内容',
      NotificationDetails(
        ohos: OhosNotificationDetails(
          OhosNotificationSlotType.SOCIAL_COMMUNICATION,
        ),
      ),
    );
  } catch (e) {
    print('显示通知失败: $e');
  }
}

九、常见问题

Q1:通知不显示怎么办?

检查以下几点:

  1. 确保已请求通知权限(OpenHarmony 必需)

    dart 复制代码
    final granted = await notifications
        .resolvePlatformSpecificImplementation<OhosFlutterLocalNotificationsPlugin>()
        ?.requestNotificationsPermission();
    if (!(granted ?? false)) {
      print('通知权限未授权');
      return;
    }
  2. 确保已正确初始化插件

  3. 确认通知渠道类型和重要性设置正确

  4. 检查通知图标资源是否存在

重要:OpenHarmony 平台必须在发送通知前请求权限,否则通知将无法显示。

Q2:定时通知不工作或待发送列表为空?

检查以下几点

  1. 确保已配置定时通知权限(最常见原因)

    json 复制代码
    // 在 module.json5 中添加
    {
      "name": "ohos.permission.PUBLISH_AGENT_REMINDER",
      "reason": "$string:reminder_reason",
      "usedScene": {
        "abilities": ["EntryAbility"],
        "when": "always"
      }
    }
  2. 确保已添加权限说明字符串

    json 复制代码
    // 在 string.json 中添加
    {
      "name": "reminder_reason",
      "value": "需要发送定时提醒通知"
    }
  3. 重新安装应用:权限配置修改后需要重新安装应用才能生效

  4. 确保已设置本地时区(定时通知必需)

    dart 复制代码
    // 初始化时区数据库
    tz.initializeTimeZones();
    
    // 获取并设置本地时区
    final String? timeZoneName = await notifications.getLocalTimezone();
    if (timeZoneName != null) {
      tz.setLocalLocation(tz.getLocation(timeZoneName));
    }
  5. OpenHarmony 系统可能会优化后台任务,导致定时通知有延迟

Q3:如何自定义通知声音?

将音频文件放入 raw 资源目录,然后在通知详情中指定:

dart 复制代码
OhosNotificationDetails(
  OhosNotificationSlotType.SOCIAL_COMMUNICATION,
  playSound: true,
  sound: OhosNotificationSound(
    OhosNotificationSoundSource.rawResource,
    'custom_sound',
  ),
);

Q4:通知点击后如何跳转页面?

在通知回调中处理导航:

dart 复制代码
onDidReceiveNotificationResponse: (response) {
  if (response.payload == 'open_detail') {
    Navigator.pushNamed(context, '/detail');
  }
},

Q5:如何获取当前显示的通知?

dart 复制代码
final activeNotifications = await notifications.getActiveNotifications();
for (var notification in activeNotifications) {
  print('活动通知: ${notification.id}, ${notification.title}');
}

注意 :OpenHarmony API 12 版本暂不支持读取 payload 字段,返回的 ActiveNotification 对象中 payload 字段将为空。


十、总结

flutter_local_notifications 库为 Flutter for OpenHarmony 开发提供了完整的本地通知能力。通过丰富的 API,开发者可以实现即时通知、定时通知、周期性通知等功能,并支持多种通知样式和交互操作。该库在鸿蒙平台上已经完成了完整的适配,支持所有核心功能,开发者可以放心使用。

重要提示

  1. 即时通知 :需要在运行时请求通知权限(requestNotificationsPermission()
  2. 定时通知
    • 需要在 module.json5 中配置 ohos.permission.PUBLISH_AGENT_REMINDER 权限
    • 需要设置本地时区(getLocalTimezone() + setLocalLocation()
    • 真机上必须向华为申请代理提醒权限,否则无法使用
  3. payload 字段:OpenHarmony API 12 暂不支持从系统读取,需自行存储管理

真机定时通知限制

  • OpenHarmony 真机对代理提醒功能有管控机制
  • 未通过华为审批的应用无法使用后台代理提醒能力
  • 需要通过邮件向华为申请代理提醒权限

十一、完整代码示例

以下是一个完整的可运行示例,展示了 flutter_local_notifications 库的核心功能:

main.dart

dart 复制代码
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化时区数据库
  tz.initializeTimeZones();
  
  final notifications = FlutterLocalNotificationsPlugin();
  await notifications.initialize(
    InitializationSettings(
      ohos: OhosInitializationSettings('@mipmap/ic_launcher'),
    ),
    onDidReceiveNotificationResponse: (NotificationResponse response) {
      debugPrint('通知被点击: ${response.payload}');
    },
  );
  
  // 设置本地时区(OpenHarmony 定时通知必需)
  final String? timeZoneName = await notifications.getLocalTimezone();
  if (timeZoneName != null) {
    tz.setLocalLocation(tz.getLocation(timeZoneName));
    debugPrint('本地时区: $timeZoneName');
  }
  
  // 请求通知权限(OpenHarmony 必需)
  final bool? granted = await notifications
      .resolvePlatformSpecificImplementation<OhosFlutterLocalNotificationsPlugin>()
      ?.requestNotificationsPermission();
  
  if (granted ?? false) {
    debugPrint('通知权限已授权');
  } else {
    debugPrint('通知权限被拒绝');
  }
  
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Local Notifications Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin();
  List<PendingNotificationRequest> _pendingNotifications = [];
  String _statusMessage = '';

  @override
  void initState() {
    super.initState();
    _loadPendingNotifications();
    _checkLaunchSource();
  }

  Future<void> _loadPendingNotifications() async {
    final pending = await _notifications.pendingNotificationRequests();
    setState(() {
      _pendingNotifications = pending;
    });
  }

  Future<void> _checkLaunchSource() async {
    final details = await _notifications.getNotificationAppLaunchDetails();
    if (details?.didNotificationLaunchApp ?? false) {
      setState(() {
        _statusMessage = '应用通过通知启动\nPayload: ${details?.notificationResponse?.payload}';
      });
    }
  }

  Future<void> _showBasicNotification() async {
    await _notifications.show(
      1,
      '基础通知',
      '这是一条基础通知内容',
      NotificationDetails(
        ohos: OhosNotificationDetails(
          OhosNotificationSlotType.SOCIAL_COMMUNICATION,
          importance: OhosImportance.high,
          playSound: true,
          enableVibration: true,
        ),
      ),
      payload: 'basic_notification',
    );
    _showMessage('基础通知已发送');
  }

  Future<void> _showBigTextNotification() async {
    await _notifications.show(
      2,
      '大文本通知',
      '展开查看更多内容',
      NotificationDetails(
        ohos: OhosNotificationDetails(
          OhosNotificationSlotType.SOCIAL_COMMUNICATION,
          styleInformation: OhosBigTextStyleInformation(
            '这是一段很长的通知内容,可以显示更多文字信息。'
            '大文本样式允许通知展开时显示更多内容,'
            '适合用于显示完整的消息内容或详细描述。',
            contentTitle: '大文本标题',
            summaryText: '点击展开',
          ),
        ),
      ),
      payload: 'big_text_notification',
    );
    _showMessage('大文本通知已发送');
  }

  Future<void> _showProgressNotification() async {
    for (int i = 0; i <= 100; i += 10) {
      await _notifications.show(
        3,
        '下载进度',
        i == 100 ? '下载完成' : '正在下载... $i%',
        NotificationDetails(
          ohos: OhosNotificationDetails(
            OhosNotificationSlotType.SERVICE_INFORMATION,
            showProgress: true,
            maxProgress: 100,
            progress: i,
            ongoing: i < 100,
            autoCancel: i == 100,
          ),
        ),
      );
      await Future.delayed(const Duration(milliseconds: 500));
    }
    _showMessage('下载完成');
  }

  Future<void> _scheduleNotification() async {
    final scheduledDate = tz.TZDateTime.now(tz.local).add(const Duration(seconds: 10));
  
    await _notifications.zonedSchedule(
      4,
      '定时通知',
      '这是10秒后的定时通知',
      scheduledDate,
      NotificationDetails(
        ohos: OhosNotificationDetails(
          OhosNotificationSlotType.SOCIAL_COMMUNICATION,
        ),
      ),
      uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
      payload: 'scheduled_notification',
    );
  
    _showMessage('定时通知已设置,10秒后发送');
    _loadPendingNotifications();
  }

  Future<void> _scheduleDailyNotification() async {
    final now = DateTime.now();
    final scheduledDate = tz.TZDateTime(
      tz.local,
      now.year,
      now.month,
      now.day,
      now.hour,
      now.minute + 1,
    );
  
    await _notifications.zonedSchedule(
      5,
      '每日提醒',
      '这是每分钟重复的提醒',
      scheduledDate,
      NotificationDetails(
        ohos: OhosNotificationDetails(
          OhosNotificationSlotType.SOCIAL_COMMUNICATION,
        ),
      ),
      uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
      matchDateTimeComponents: DateTimeComponents.time,
    );
  
    _showMessage('每日通知已设置');
    _loadPendingNotifications();
  }

  Future<void> _cancelNotification(int id) async {
    await _notifications.cancel(id);
    _showMessage('通知已取消');
    _loadPendingNotifications();
  }

  Future<void> _cancelAllNotifications() async {
    await _notifications.cancelAll();
    _showMessage('所有通知已取消');
    _loadPendingNotifications();
  }

  void _showMessage(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Local Notifications 演示'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            if (_statusMessage.isNotEmpty)
              Card(
                color: Colors.blue.shade100,
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Text(_statusMessage),
                ),
              ),
            const SizedBox(height: 16),
  
            const Text(
              '发送通知',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
  
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                ElevatedButton.icon(
                  onPressed: _showBasicNotification,
                  icon: const Icon(Icons.notifications),
                  label: const Text('基础通知'),
                ),
                ElevatedButton.icon(
                  onPressed: _showBigTextNotification,
                  icon: const Icon(Icons.text_fields),
                  label: const Text('大文本'),
                ),
                ElevatedButton.icon(
                  onPressed: _showProgressNotification,
                  icon: const Icon(Icons.download),
                  label: const Text('进度通知'),
                ),
                ElevatedButton.icon(
                  onPressed: _scheduleNotification,
                  icon: const Icon(Icons.schedule),
                  label: const Text('定时通知'),
                ),
                ElevatedButton.icon(
                  onPressed: _scheduleDailyNotification,
                  icon: const Icon(Icons.today),
                  label: const Text('每日通知'),
                ),
              ],
            ),
            const SizedBox(height: 24),
  
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const Text(
                  '待发送通知',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                TextButton.icon(
                  onPressed: _cancelAllNotifications,
                  icon: const Icon(Icons.clear_all),
                  label: const Text('取消全部'),
                ),
              ],
            ),
            const SizedBox(height: 8),
  
            if (_pendingNotifications.isEmpty)
              const Card(
                child: Padding(
                  padding: EdgeInsets.all(16),
                  child: Text('暂无待发送通知'),
                ),
              )
            else
              ListView.builder(
                shrinkWrap: true,
                physics: const NeverScrollableScrollPhysics(),
                itemCount: _pendingNotifications.length,
                itemBuilder: (context, index) {
                  final notification = _pendingNotifications[index];
                  return Card(
                    child: ListTile(
                      leading: CircleAvatar(
                        child: Text('${notification.id}'),
                      ),
                      title: Text(notification.title ?? '无标题'),
                      subtitle: Text(notification.body ?? '无内容'),
                      trailing: IconButton(
                        icon: const Icon(Icons.close),
                        onPressed: () => _cancelNotification(notification.id),
                      ),
                    ),
                  );
                },
              ),
            const SizedBox(height: 24),
  
            const Text(
              '通知渠道类型',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
  
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    _buildSlotInfo('SOCIAL_COMMUNICATION', '社交通信', '消息、聊天'),
                    _buildSlotInfo('SERVICE_INFORMATION', '服务信息', '系统更新、下载'),
                    _buildSlotInfo('CONTENT_INFORMATION', '内容信息', '新闻、推荐'),
                    _buildSlotInfo('LIVE_VIEW', '直播', '直播通知'),
                    _buildSlotInfo('CUSTOMER_SERVICE', '客户服务', '客服消息'),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
  
  static Widget _buildSlotInfo(String type, String name, String usage) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        children: [
          SizedBox(
            width: 180,
            child: Text(type, style: const TextStyle(fontWeight: FontWeight.bold)),
          ),
          Expanded(
            child: Text('$name - $usage'),
          ),
        ],
      ),
    );
  }
}

运行此示例后,您将看到一个完整的本地通知演示界面,包含基础通知、大文本通知、进度通知、定时通知、每日通知等功能,以及待发送通知列表管理。点击通知按钮即可发送对应类型的通知。

相关推荐
哈__2 小时前
Flutter for OpenHarmony三方库适配实战:flutter_tts 文字转语音
flutter
木子雨廷2 小时前
Flutter Redux 项目实战
flutter
AI_零食2 小时前
Flutter 框架跨平台鸿蒙开发 - 颜色听觉化应用
学习·flutter·信息可视化·开源·harmonyos
2301_822703202 小时前
大学生体质健康测试全景测绘台:基于鸿蒙Flutter的多维数据可视化与状态管理响应架构
算法·flutter·信息可视化·架构·开源·harmonyos·鸿蒙
独特的螺狮粉2 小时前
生命科学实验室经费极简记账簿:基于鸿蒙Flutter的极简主义状态响应与流式布局架构
flutter·华为·架构·开源·harmonyos
HH思️️无邪2 小时前
Flutter + iOS 实战指南:教程视频 PiP + 退桌面(可复用模板)
flutter·ios
提子拌饭1332 小时前
红细胞代偿性增殖与睡眠剥夺的对照演算引擎:基于鸿蒙Flutter的微观流体力学粒子渲染架构
flutter·华为·架构·开源·harmonyos·鸿蒙
浮芷.2 小时前
Flutter 框架跨平台鸿蒙开发 - 智能家电故障诊断应用
运维·服务器·科技·flutter·华为·harmonyos·鸿蒙
浮芷.2 小时前
Flutter 框架跨平台鸿蒙开发 - 急救指南应用
学习·flutter·华为·harmonyos·鸿蒙