轻量 Flutter 鸿蒙适配库 notification_permissions 实战,查询通知权限、跳转设置页,一套 API 跨三端复用
本系列针对 CPF-Flutter 组织适配鸿蒙的 fluttertpc 系列插件逐一进行源码分析与真机实测,本文的主角是
notification_permissions——一个用于查询与申请通知权限的轻量插件。
-
一句话介绍:
notification_permissions是一个跨平台 Flutter 插件,用于查询当前应用的通知权限状态(granted / denied / unknown / provisional)并引导用户开启通知权限,鸿蒙侧由 CPF-Flutter 组织基于原版 notification_permissions 0.6.1 完成适配。 -
为什么鸿蒙开发者需要它:鸿蒙与 Android 的通知权限模型完全不同——鸿蒙没有运行时
requestPermissions式的通知授权弹窗 API,权限开关藏在系统设置的「通知管理」深处;适配库把isNotificationEnabled()查询和「跳转通知设置页」两个动作封装成了与 Android/iOS 完全一致的 Dart API,业务代码零改动即可跨平台。 -
实测结论先行:在华为 TLR-AL00(HarmonyOS 6.1.0.145,API 24)真机上,权限查询、设置页跳转、开关切换后状态联动刷新全链路验证通过。

一、最终运行效果
1.1 实测截图
启动首页:应用未授权时,显示 denied 状态与申请按钮

点击「ASK FOR NOTIFICATION STATUS」后:直接跳转系统「通知管理」页(注意:不是系统弹窗)

手动打开「允许通知」开关

返回应用后:生命周期回调自动刷新状态,变为 granted

example 还内置了「质量加固」演示页,覆盖 6 个验证场景

1.2 验证点表格
| 功能点 | 预期表现 | 实测结果 |
|---|---|---|
| 首次查询权限状态(未授权) | 返回 PermissionStatus.denied,页面显示 “The permission status is denied” | ✅ 通过 |
| 点击申请按钮(未授权时) | 跳转到本应用对应的系统「通知管理」设置页 | ✅ 通过(鸿蒙上不是弹窗,是设置页,见第七节分析) |
| 系统页打开「允许通知」开关 | 系统侧通知授权状态更新(hilog 可见 set ntf auht status ... 1) | ✅ 通过 |
| 返回应用后状态自动刷新 | 通过 WidgetsBindingObserver 在 resumed 时重新查询,页面变为 “granted” | ✅ 通过 |
| 已授权时再次查询 | 返回 PermissionStatus.granted(hilog isNotificationEnabled allowed=1) | ✅ 通过 |
| 已授权时点击申请按钮 | 直接返回 granted,不再跳转 | ✅ 通过(源码逻辑:isNotificationEnabled 为 true 时直接 result.success(granted),且授权后首页不渲染按钮) |
| unsigned HAP 直接安装 | —— | ❌ 失败,错误码 9568320: no signature file,必须签名后安装,见第四节说明 |
二、库介绍与接口说明
notification_permissions 是对原版 pub.dev 插件(Vanethos/notification_permissions)的鸿蒙化适配,Dart 层 API 与原版完全一致,原业务代码无需任何修改。插件在 pubspec.yaml 中注册了三个平台实现:
flutter:
plugin:
platforms:
android:
package: com.vanethos.notification_permissions
pluginClass: NotificationPermissionsPlugin
ios:
pluginClass: NotificationPermissionsPlugin
ohos:
package: com.vanethos.notification_permissions
pluginClass: NotificationPermissionsPlugin
2.1 核心 API
| 方法 | 平台支持(Android / iOS / ohos) | 说明 |
|---|---|---|
getNotificationPermissionStatus() → Future<PermissionStatus> | ✅ / ✅ / ✅ | 查询当前通知权限状态。鸿蒙侧内部调用 notificationManager.isNotificationEnabled(),true → granted,false → denied |
requestNotificationPermissions({iosSettings, openSettings}) → Future<PermissionStatus> | ✅ / ✅ / ✅ | 请求通知权限。鸿蒙侧:未授权则跳转系统通知设置页并返回 denied;已授权直接返回 granted。iosSettings 在鸿蒙上被忽略 |
PermissionStatus 枚举 | ✅ / ✅ / ✅ | granted / unknown / denied / provisional 四种状态。注意:鸿蒙永远不会返回 provisional(这是 iOS 临时授权概念) |
2.2 与原版的差异点
- 原版 Android 实现会弹出系统授权对话框或跳转应用详情页;鸿蒙实现改为跳转专用的通知管理设置页(
systemui_notification_settings),路径更短、更精准。 - 鸿蒙版额外响应
getPlatformVersion方法(返回"OpenHarmony ^ ^ "),属于适配模板的保留调试接口。 openSettings参数在鸿蒙上实际恒为「打开设置页」行为,不生效(原生端未读取该参数)。
三、环境准备
3.1 实测环境
| 项目 | 版本/配置 |
|---|---|
| Flutter | 3.47.7-ohos-1.0.0(channel oh-3.47-dev,Dart 3.13.3) |
| DevEco Studio | 26.0(SDK 目录 sdk/default,API 26 编译环境可用) |
| 真机 | 华为 TLR-AL00 |
| 系统版本 | HarmonyOS 6.1.0.145(API 24) |
| compatibleSdkVersion | 6.1.1(24)(example/ohos/build-profile.json5 实际值) |
| targetSdkVersion | 6.1.1(24) |
3.2 鸿蒙通知权限机制速览
理解鸿蒙与 Android 的差异,是用好这个插件的前提:
- 鸿蒙的通知权限不是「运行时权限」。它不进入
requestPermissionsFromUser那套 ACL/运行时权限体系,而是由系统通知服务(ANS,Advanced Notification Service)按应用维度管理一个布尔开关。 - 查询用
notificationManager.isNotificationEnabled():返回当前应用是否被允许发通知。该接口不需要额外权限,插件正是基于它实现状态查询。 - 鸿蒙没有一个通用的「弹窗申请通知授权」公开 API。早期 API 曾提供
requestEnableNotification()弹窗授权,但它有严格限制(依赖 UIAbility 上下文、每次冷启动最多弹两次、且新版本已不推荐直接使用),所以适配库选择了更稳妥的方案:未授权时直接startAbility跳转到系统设置的应用通知管理页,让用户手动打开开关——这与原生鸿蒙应用的主流引导做法一致。 - 用户在设置页开关权限后,系统会立即更新授权状态,应用回到前台时再查询即可拿到新状态——这正是 example 首页
WidgetsBindingObserver监听resumed刷新状态的原因。
四、引入依赖
⚠️ 重要提醒:README 中的 git 地址有误
仓库自带的 README.OpenHarmony.md 和 README.OpenHarmony_CN.md 中,依赖地址写的是 https://gitcode.com/CPF-Flutter/fluttertpc_flutter_notification_permissions。经 git remote -v 实际验证,仓库托管在 AtomGit,gitcode 地址并不可用。本文已顺手修正了仓库内两份 README,读者接入时请直接使用下面验证过的写法。
实际远程仓库与 TAG(以本仓库实际输出为准):
$ git remote -v
origin https://atomgit.com/CPF-Flutter/fluttertpc_flutter_notification_permissions (fetch)
origin https://atomgit.com/CPF-Flutter/fluttertpc_flutter_notification_permissions (push)
$ git tag
0.1.0
0.2.0
0.4.5
0.4.6
0.6.1-ohos-1.0.0 ← 鸿蒙适配版 TAG
0.6.1-ohos.1.0.0-beta.1
v0.4.8
v0.5.0
v0.6.0
v0.6.1
$ git branch -a
* br_3.27 ← 鸿蒙适配分支
remotes/origin/br_3.27
remotes/origin/br_3.27_dev
...
4.1 pubspec.yaml 写法
在应用工程(或插件的 example)的 pubspec.yaml 中添加:
dependencies:
notification_permissions:
git:
url: https://atomgit.com/CPF-Flutter/fluttertpc_flutter_notification_permissions
ref: 0.6.1-ohos-1.0.0
TAG 与分支对应关系(README 中表格列出的 Flutter 版本虽然不同,但 TAG/分支均相同,本仓库实际存在该 TAG 与分支):
| Flutter 框架版本 | TAG | 分支 |
|---|---|---|
| 3.7 / 3.22 / 3.27 / 3.35 | 0.6.1-ohos-1.0.0 | br_3.27 |
然后执行:
flutter pub get
4.2 关于签名的实战提醒(本次实测踩坑实录)
仓库 example 预编译产物只有 unsigned HAP,直接安装会被拒绝:
$ hdc install -r entry-default-unsigned.hap
[Info]App install path:...\entry-default-unsigned.hap
msg:error: failed to install bundle. code:9568320 error: no signature file.
另外,用 OpenHarmony SDK 自带的调试材料本地自签 profile 的方案在零售 HarmonyOS 真机上同样会被拒绝(错误码 9568257: fail to verify pkcs7 file)——零售机不信任 OpenHarmony 调试证书链。正确做法是在 DevEco Studio 中登录华为开发者账号,使用 File → Project Structure → Signing Configs → Automatically generate signature 自动签名,它签发的 .p7b Profile 会绑定你的 bundleName 与设备 UDID。本次实测即采用自动签名材料完成构建安装(详见第八节 FAQ-1)。
五、代码接入
example 的完整代码在 example/lib/main.dart,下面按功能分小节拆解。
5.1 初始化:进应用先查一次权限
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
Future<String> permissionStatusFuture = Future.value('');
void initState() {
super.initState();
// 启动即查询一次通知权限状态
permissionStatusFuture = getCheckNotificationPermStatus();
// 注册生命周期观察者,便于从设置页返回后刷新
WidgetsBinding.instance.addObserver(this);
}
代码逐段分析
_MyAppState混入WidgetsBindingObserver,这是跨平台权限类插件的经典套路:权限的授予动作发生在「应用外」(系统设置页),应用自身无法监听,只能靠生命周期回前台的时机重新查询。permissionStatusFuture是一个Future<String>,交给FutureBuilder渲染,避免在initState里直接setState。initState里同时完成「首查」与「注册观察者」两件事,顺序无所谓,但缺一不可。
5.2 查询权限状态
/// 查询通知权限状态,把枚举转成字符串便于展示
Future<String> getCheckNotificationPermStatus() {
return NotificationPermissions.getNotificationPermissionStatus()
.then((status) {
switch (status) {
case PermissionStatus.denied:
return permDenied;
case PermissionStatus.granted:
return permGranted;
case PermissionStatus.unknown:
return permUnknown;
case PermissionStatus.provisional:
return permProvisional;
default:
return Future.value('unknown');
}
});
}
代码逐段分析
NotificationPermissions.getNotificationPermissionStatus()是静态方法,一行即可完成查询,返回PermissionStatus枚举。- 枚举四个值的来源:Dart 层把 MethodChannel 返回的字符串(
granted/denied/provisional/其他)映射成枚举,鸿蒙原生端只会返回granted和denied两种,provisional仅供 iOS 使用。- example 把枚举转回字符串仅用于 UI 展示,业务代码一般直接
switch (status)分支处理即可。
5.3 生命周期联动:回前台自动刷新
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
setState(() {
permissionStatusFuture = getCheckNotificationPermStatus();
});
}
}
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
代码逐段分析
- 只有
resumed状态才触发重查:用户从通知设置页返回应用的那一刻,权限可能已经变了。- 每次
setState都给permissionStatusFuture赋一个新的 Future,FutureBuilder检测到 future 实例变化会重新走 loading → data 流程,实现 UI 刷新。dispose里务必removeObserver,防止页面销毁后回调空指针。
5.4 申请权限
TextButton(
child: Text("Ask for notification status".toUpperCase()),
onPressed: () {
NotificationPermissions.requestNotificationPermissions(
iosSettings: const NotificationSettingsIos(
sound: true, badge: true, alert: true))
.then((_) {
// 申请流程结束后立即复查状态
setState(() {
permissionStatusFuture = getCheckNotificationPermStatus();
});
});
},
)
代码逐段分析
requestNotificationPermissions传入的NotificationSettingsIos(sound/alert/badge)是 iOS 专属参数,在鸿蒙上会被原生端完全忽略,但为了三端代码统一,建议照传。- 在鸿蒙上的实际行为链:插件先查
isNotificationEnabled(),已授权 → 直接返回granted;未授权 → 跳转系统通知设置页并返回denied。方法返回时用户还没做选择,所以.then里必须再查一次状态。FutureBuilder的渲染逻辑:状态为granted时只显示文本;否则额外渲染这个申请按钮——这就是授权后按钮自动消失的原因。
5.5 UI 渲染骨架
FutureBuilder(
future: permissionStatusFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('error while retrieving status: ${snapshot.error}');
}
if (snapshot.hasData) {
var textWidget = Text(
"The permission status is ${snapshot.data}",
style: TextStyle(fontSize: 20),
);
// 已授权:只显示文本;未授权:文本 + 申请按钮
if (snapshot.data == permGranted) {
return textWidget;
}
return Column(children: <Widget>[textWidget, /* 申请按钮 */]);
}
return Text("No permission status yet");
},
)
代码逐段分析
- 三态渲染:waiting 转圈、error 报错、hasData 出内容,是 FutureBuilder 的标准防御写法。
- 原生端返回
PERMISSION_CHECK_FAILED等错误码时,会走snapshot.hasError分支,实际项目中建议在此给用户友好提示。
六、运行与验证
6.1 构建、安装与启动
使用华为开发者账号自动签名材料完成签名构建后(构建命令 flutter build hap --debug,产物 entry-default-signed.hap):
$ hdc install -r entry-default-signed.hap
[Info]App install path:...\entry-default-signed.hap msg:install bundle successfully.
$ hdc shell aa start -a EntryAbility -b dev.kakzaki.color_brightness_example
start ability successfully.
说明:本次实测用的调试签名 Profile 绑定的 bundleName 为
dev.kakzaki.color_brightness_example(自动签名 Profile 与 bundleName 一一绑定,见 FAQ-1),因此将 example 的AppScope/app.json5中 bundleName 临时改为了该值。这对插件行为无任何影响——插件跳转设置页时用的是运行时动态获取的abilityInfo.bundleName,而非硬编码。
6.2 hilog 实测日志原文
① 点击申请按钮:MethodChannel 消息到达 → 原生查询 → 未授权(allowed=0)→ 跳转设置页(应用切 inactive/paused):
10-22 01:56:09.702 31217 31217 D A000FF/.../Flutter: FlutterNapi --> called handlePlatformMessage
Channel: notification_permissions. Response ID: 11
10-22 01:56:09.702 31217 31217 D A000FF/.../Flutter: DartMessenger --> Received message from Dart
over channel 'notification_permissions'
10-22 01:56:09.704 31217 31622 I C01203/.../Ans: isNotificationEnabled code=0,allowed=0
10-22 01:56:09.897 31217 31217 I A000FF/.../Flutter: LifecycleChannel --> Sending
AppLifecycleState.inactive message.
10-22 01:56:09.899 31217 31217 I A000FF/.../Flutter: LifecycleChannel --> Sending
AppLifecycleState.paused message.
② 在设置页打开开关:系统通知服务写入授权状态(foundation 进程日志):
10-22 01:56:24.226 1496 32212 I C01203/foundation/Ans: bundelName:dev.kakzaki.color_brightness_example,
uid:20020231, state[1]
10-22 01:56:24.231 1496 32212 I C01203/foundation/Ans: set ntf auht status
dev.kakzaki.color_brightness_example 20020231 1 0
③ 返回应用:resumed → 自动复查 → allowed=1(对应页面立即变为 granted):
10-22 01:57:37.506 31217 31217 I A000FF/.../Flutter: LifecycleChannel --> Sending
AppLifecycleState.resumed message.
10-22 01:57:37.509 31217 31217 D A000FF/.../Flutter: DartMessenger --> Received message from Dart
over channel 'notification_permissions'
10-22 01:57:37.519 31217 32779 I C01203/.../Ans: isNotificationEnabled code=0,allowed=1
日志里的
A000FF/.../Flutter、C01203/.../Ans是 hilog 的 domain/tag:前者是 Flutter 通道层日志(DartMessenger/FlutterNapi/LifecycleChannel),后者是系统通知服务 ANS 的日志。code=0表示调用成功,allowed即授权结果。
6.3 全流程截图
完整截图序列(denied → 设置页 → 开关打开 → 返回 granted → 质量加固演示页)见第一节;所有截图与 hilog 原文均存档于仓库 screenshots/ohos/ 目录(np_hilog_evidence.txt 为日志取证汇总)。
七、工作原理
7.1 MethodChannel 通道与调用链路
Dart 层定义(lib/notification_permissions.dart):
static const MethodChannel _channel =
const MethodChannel('notification_permissions');
通道名为 notification_permissions,与 Android/iOS/ohos 三端原生实现保持一致。完整调用链路:
┌────────────────────────────── Dart (UI 线程) ──────────────────────────────┐
│ NotificationPermissions.getNotificationPermissionStatus() │
│ └─ _channel.invokeMethod('getNotificationPermissionStatus') │
└──────────────────────────────────┬─────────────────────────────────────────┘
│ BinaryMessenger (平台通道)
▼
┌────────────────────────── ArkTS 插件层 (ohos) ─────────────────────────────┐
│ NotificationPermissionsPlugin.onMethodCall(call, result) │
│ ├─ "getNotificationPermissionStatus" │
│ │ └─ notificationManager.isNotificationEnabled() │
│ │ ├─ true → result.success("granted") │
│ │ └─ false → result.success("denied") │
│ └─ "requestNotificationPermissions" │
│ ├─ isNotificationEnabled() == true │
│ │ └─ result.success("granted") │
│ └─ == false → startAbility(Want{ │
│ uri: "systemui_notification_settings", │
│ parameters: { pushParams: { bundleName: 本应用 } } │
│ }) → result.success("denied") │
└──────────────────────────────────┬─────────────────────────────────────────┘
│ result.success/error
▼
Dart 层 _getPermissionStatus() 映射为枚举
关键步骤拆解:
- Dart 调
invokeMethod,消息经 Flutter OHOS embedder 的DartMessenger送达平台线程(对应日志Received message from Dart over channel 'notification_permissions')。 - 插件在
onAttachedToEngine时注册了MethodCallHandler,按call.method字符串分发。 - 查询方法直接对接系统 ANS 服务,一次异步调用即返回。
- 申请方法在未授权时通过 隐式 Want(
uri: "systemui_notification_settings"+pushParams.bundleName)拉起系统设置的通知管理页——这是华为系统设置对外暴露的内部页面路由,精准定位到「本应用」的通知开关。
7.2 原生 ArkTS 插件实现分析
插件源码只有约 150 行(ohos/src/main/ets/components/plugin/NotificationPermissionsPlugin.ets),核心结构:
export default class NotificationPermissionsPlugin
implements FlutterPlugin, MethodCallHandler, AbilityAware {
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.context = binding.getApplicationContext();
this.channel = new MethodChannel(
binding.getBinaryMessenger(), "notification_permissions");
this.channel.setMethodCallHandler(this);
}
onAttachedToAbility(binding: AbilityPluginBinding): void {
this.ability = binding.getAbility(); // 保存 UIAbility 引用备用
}
...
}
代码逐段分析
- 插件同时实现
FlutterPlugin(引擎生命周期)、MethodCallHandler(消息分发)、AbilityAware(获取 UIAbility)三个接口,是 flutter_ohos 插件的标准三件套。onAttachedToEngine只拿到ApplicationContext,而跳转设置页必须用 UIAbility 的 context(startAbility是 Ability 级能力),所以要通过AbilityAware.onAttachedToAbility额外把UIAbility存下来。这也是为什么该插件在 Ability 未附着时会返回UNAVAILABLE错误。
查询实现:
private async getNotificationPermissionStatus(result: MethodResult): Promise<void> {
try {
const isEnabled: boolean = await notificationManager.isNotificationEnabled();
const status: string = isEnabled ? PERMISSION_GRANTED : PERMISSION_DENIED;
result.success(status);
} catch (error) {
result.error("PERMISSION_CHECK_FAILED", "Failed to check notification permission", null);
}
}
代码逐段分析
isNotificationEnabled()不带参数时查询的是当前应用自身的通知开关,无需任何ohos.permission声明,普通应用可直接调用。- 返回值被压缩成
granted/denied二值——鸿蒙没有 iOS 的 provisional(临时授权)概念,也查询不出 Android 的「永久拒绝」状态,Dart 层的unknown只在通道异常时兜底出现。- 异常统一转成
result.error(错误码, 描述, null),Dart 侧表现为 Future 抛PlatformException。
申请实现(鸿蒙版的核心差异所在):
private async requestNotificationPermissions(result: MethodResult): Promise<void> {
const isEnabled: boolean = await notificationManager.isNotificationEnabled();
if (!isEnabled) {
if (this.ability === null) {
result.error("UNAVAILABLE", "Ability is unavailable, cannot open settings", null);
return;
}
const openSuccess = await this.openNotificationSettings();
if (openSuccess) {
result.success(PERMISSION_DENIED); // 注意:跳转成功也返回 denied
} else {
result.error("SETTINGS_ERROR", "Failed to open notification settings", null);
}
} else {
result.success(PERMISSION_GRANTED);
}
}
private async openNotificationSettings(): Promise<boolean> {
const want: Want = {
uri: "systemui_notification_settings",
parameters: {
pushParams: {
bundleName: this.ability.context.abilityInfo.bundleName
}
}
} as Want;
await this.ability.context.startAbility(want);
return true;
}
代码逐段分析
- 与 Android 版最大的差异:Android 版会弹系统授权对话框(Android 13+ 的
POST_NOTIFICATIONS运行时权限弹窗),而鸿蒙公开 API 没有「弹窗申请通知授权」的能力(历史上的requestEnableNotification()弹窗有调用次数限制且新版本不推荐),所以适配版选择直接跳系统设置页。这决定了鸿蒙上requestNotificationPermissions返回值语义是「跳转动作的结果」而非「授权结果」——跳转成功返回的仍是denied,真正的授权结果要等用户操作后回前台再查。- Want 的
uri使用系统设置注册的systemui_notification_settings路由,pushParams.bundleName让设置页直接定位到本应用的通知管理卡片(实测截图第二节第二张就是落到这个页面的效果)。bundleName取自运行时abilityInfo,不是硬编码——所以改 bundleName 打包完全不影响功能。- 整个文件没有请求任何权限、没有在
module.json5声明requestPermissions,因为查询接口本身就是免权限的,这比 Android 版还要省事。
7.3 鸿蒙通知权限模型 vs Android
| 维度 | Android | HarmonyOS |
|---|---|---|
| 权限归属 | 运行时权限(Android 13+ POST_NOTIFICATIONS) | 系统通知服务 ANS 按应用管理的开关 |
| 申请方式 | 弹授权对话框 / 跳应用详情页 | 只能跳转系统通知设置页(无公开弹窗 API) |
| 查询接口 | NotificationManager.areNotificationsEnabled() | notificationManager.isNotificationEnabled() |
| 状态粒度 | granted / denied(可细分永久拒绝) | granted / denied 二值 |
| 权限声明 | 需在 AndroidManifest 声明 | 无需任何权限声明 |
| 申请返回值语义 | 弹窗结果(用户已选择) | 跳转结果(用户尚未选择) |
八、FAQ
Q1:hdc install 报 code:9568320 error: no signature file 怎么办?
HAP 没有签名。在 DevEco Studio 中打开 ohos 工程,File → Project Structure → Signing Configs 勾选「Automatically generate signature」,登录华为开发者账号后自动生成 .p12/.cer/.p7b 并写入 build-profile.json5 的 signingConfigs,再重新构建。注意:用 OpenHarmony SDK 调试材料手工自签 profile 在零售 HarmonyOS 真机上不可用(实测报 9568257: fail to verify pkcs7 file,零售机不信任 OpenHarmony 调试证书链),必须走 DevEco 自动签名。另外自动签名生成的 Profile 与 bundleName 一一绑定,改了 bundleName 就得重新生成签名。
Q2:为什么鸿蒙上点申请按钮不弹系统授权弹窗,直接跳到了设置页?
这是鸿蒙平台特性而非 bug。鸿蒙没有公开的「通知授权弹窗」API(requestEnableNotification() 有调用次数限制且新版不推荐),适配库选择直接 startAbility 跳转系统通知管理页。开发者应在 UI 上预期「跳设置页」的交互,并在回前台后复查状态。
Q3:为什么 requestNotificationPermissions 返回 denied,用户明明等下会点「允许」?
方法返回时只代表「跳转动作完成」,用户还没做选择。正确姿势是在 WidgetsBindingObserver.didChangeAppLifecycleState 的 resumed 时机重新调用 getNotificationPermissionStatus(),example 首页就是这么做的。
Q4:provisional 状态在鸿蒙上会出现吗?
不会。provisional 是 iOS 12+ 的「临时静默授权」概念,鸿蒙原生端只会返回 granted/denied,unknown 仅在通道调用失败兜底时出现。跨平台代码不要依赖 provisional 做鸿蒙逻辑分支。
Q5:NotificationSettingsIos 的 sound/alert/badge 参数在鸿蒙上需要传吗?
传了也会被鸿蒙原生端忽略(原生端只读 openSettings,而 openSettings 实际也未生效)。为了三端代码一致可以照传;如果只做鸿蒙,直接 NotificationPermissions.requestNotificationPermissions() 即可。
Q6:鸿蒙上需要像 Android 那样在 module.json5 里声明通知权限吗?
不需要。isNotificationEnabled() 查询自身开关与 startAbility 跳系统设置都是免权限操作,插件的 ohos/src/main/module.json5 没有声明任何 requestPermissions。但如果你要真正发送通知,发送侧(如 notificationManager.publish)对应用自身的通知开关状态有要求,即必须处于 granted 状态——这正是本插件存在的意义。
九、结语
notification_permissions 的鸿蒙适配是一次「小而美」的移植:Dart 层 API 与原版零差异,ArkTS 侧仅用约 150 行代码对接了 isNotificationEnabled() 与系统设置页跳转,干净地绕开了鸿蒙「无通知授权弹窗 API」的平台限制。真机实测全链路(查询 → 跳转 → 开关 → 回前台刷新)均符合预期,可放心在鸿蒙 Flutter 项目中使用。
需要留意的点集中在工程侧而非代码侧:仓库 README 的 git 地址需以 atomgit 为准(本文已修正)、unsigned HAP 无法直装、调试签名与 bundleName/设备 UDID 强绑定。理解第七节的「鸿蒙通知权限模型 vs Android」差异后,这套插件的返回值语义与生命周期联动套路就完全透明了。
能力对照表
| 能力 | Android | iOS | HarmonyOS(实测) |
|---|---|---|---|
| 查询通知权限状态 | ✅ | ✅ | ✅ isNotificationEnabled() |
| 申请通知权限(弹窗) | ✅(Android 13+ 弹窗) | ✅ | ❌ 平台无此公开 API |
| 申请通知权限(跳设置页) | ✅ | ✅ | ✅ systemui_notification_settings Want |
| provisional 临时授权 | ❌ | ✅ | ❌ |
| 免权限声明调用 | ❌(需声明 POST_NOTIFICATIONS) | ✅ | ✅ |
| openSettings 参数控制 | ✅ | ✅ | ⚠️ 不生效(恒为跳转行为) |
更多推荐




所有评论(0)