Flutter 鸿蒙适配实战:用 ringer_mode 三方库在 OpenHarmony 上实现设备铃声模式查询与监听
Flutter 鸿蒙适配实战:用 ringer_mode 三方库在 OpenHarmony 上实现设备铃声模式查询与监听
Flutter 社区地址: https://atomgit.com/CPF-Flutter/flutter_flutter
github三方库地址:https://github.com/alxmoroz/flutter_ringer_mode
pub地址:https://pub.dev/packages/ringer_mode
适配后地址:https://atomgit.com/oh-flutter/ringer_mode
库版本:ringer_mode 1.0.2(OpenHarmony 适配版)|验证环境:Flutter 鸿蒙 SDK 3.44.9(oh-3.44.9-dev)|DevEco Studio 26.0.0.821 | 设备:DevEco 模拟器 Pura X View | HarmonyOS 7.0.0.106(API 26)
ringer_mode 用于查询和监听设备的铃声模式(静音 / 振动 / 响铃)。它原本是 Android-only 的三方库(iOS 没有对应 API,鸿蒙侧也长期空白)。本文基于 ringer_mode 1.0.2 补上 OpenHarmony 实现,在 ArkTS 层调 @ohos.multimedia.audio.AudioVolumeGroupManager.getRingerMode() 读取当前模式 + 监听 ringerModeChange 事件流。文中的代码与效果均在 DevEco 模拟器(Pura X View,HarmonyOS 7.0.0.106 / API 26)上验证通过。


一、环境搭建
本章不重复展开,直接引用官方文档:Flutter OH 开发环境搭建指导。
完成后用 flutter doctor -v 验证,Flutter 与 HarmonyOS toolchain 两项均为 [√] 即可。本文实际使用的版本:Flutter OH oh-3.44.9-dev(commit 77e0c8d13b)、DevEco Studio 26.0.0.821、HarmonyOS SDK API 26。


二、应用背景
2.1 当前的应用场景与痛点
- 企业会议应用:检测设备是否处于静音/振动模式,自动提示用户检查
- 媒体/游戏类应用:根据铃声模式做差异化引导(静音时不弹通知音)
- 个性化设置引导:检测到设备静音时主动告知"开启铃声以获得完整体验"
- Do Not Disturb 类工具:核心功能就是检测 + 切换系统铃声模式
痛点:Flutter 生态里 ringer_mode 仅支持 Android(iOS 没有系统级 ringer API,鸿蒙侧长期无适配)。任何在鸿蒙上做铃声模式感知/引导的需求都没现成方案。
2.2 为什么需要这个库
自己写插件要处理 ArkTS 系统音频管理 API、Ability 生命周期、EventChannel 桥接。ringer_mode 的鸿蒙适配版由 CPF-Flutter 社区维护,一行 git 依赖即可获得与 Android 完全一致的 API。
2.3 解决什么问题
一句话总结:让 Flutter 应用在鸿蒙上以与 Android 一致的 API 查询和监听设备铃声模式。具体提供:
- 当前模式查询(
getRingerMode) - 模式变化事件流(
ringerModeStream,用户去系统设置切换时自动推送) - 模式切换(
setRingerMode)— 见 FAQ/已知限制
三、功能介绍
| 功能 | API | 说明 | 适用场景 |
|---|---|---|---|
| 模式查询 | getRingerMode() | 返回 Future<RingerMode>(silent/vibrate/normal) | 显示当前模式、引导用户去系统设置 |
| 事件流 | ringerModeStream | Stream<RingerMode>,订阅后自动推送 | 实时监听用户在系统设置里切换铃声模式 |
| 模式切换 | setRingerMode(mode) | 返回 Future<bool>(鸿蒙侧已知限制:返回 false,详见 §5) | 引导用户去系统设置 |

四、使用方法
4.1 在应用中引入三方库(AtomGit 链接方式)
dependencies:
flutter:
sdk: flutter
ringer_mode:
git:
url: https://atomgit.com/weixin_52908342/ringer_mode.git
ref: <commit>
执行 flutter pub get。注意:上游主包(Dart 层)原生就是 platform-agnostic 的(Dart 代码不区分 Android/鸿蒙),不需要 dependency_overrides。
4.2 调用接口实现功能
4.2.1 RingerMode 枚举与字符串协议
上游 Dart 端定义 RingerMode { silent, vibrate, normal },与 Android AudioManager.RINGER_MODE_* 数值一一对应(silent=0, vibrate=1, normal=2)。本适配在 ArkTS 层将 AudioRingMode 枚举直接映射到相同的小写字符串,跨端一致性无需 Dart 任何改动。
4.2.2 getRingerMode():查询当前模式
功能说明:异步 getter,返回当前铃声模式枚举值。
final mode = await RingerModeService.getRingerMode();
print('当前模式:${mode.name}'); // "silent" / "vibrate" / "normal"
运行效果:

demo 首屏:橙色 AppBar + 绿色响铃图标 + 大字"响铃" + RingerMode.normal + getRingerMode() 副标题。下方三个按钮(静音/振动/响铃,"响铃"高亮表示当前选中)。底部暗色事件流卡显示 [19:36:56] getRingerMode() → normal(订阅时立即收到一次首播事件)
4.2.3 ringerModeStream:事件流
功能说明:Stream<RingerMode>,订阅一次永久推送。用户去系统设置 → 声音和振动 切换铃声模式时,无需手动刷新即自动更新。
StreamSubscription<RingerMode>? _sub;
void initState() {
super.initState();
RingerModeService.getRingerMode().then((m) => setState(() => _mode = m));
_sub = RingerModeService.ringerModeStream.listen((m) {
setState(() => _mode = m);
});
}
void dispose() {
_sub?.cancel();
super.dispose();
}
运行效果:

在鸿蒙系统设置 → 声音和振动 切换为"静音"后回到 demo:顶部大字"静音" + 灰色铃铛+斜线图标 + RingerMode.silent + 状态栏右上角铃铛符号消失。事件流日志新增一行 [19:39:43] ringerModeStream → silent —— 真实的 ringerModeChange 事件(无需任何手动刷新就推送到 Dart 层)
4.2.4 setRingerMode(mode):切换模式(已知限制)
功能说明:尝试设置设备铃声模式为指定值。
final ok = await RingerModeService.setRingerMode(RingerMode.vibrate);
// 鸿蒙端固定返回 false
运行效果:

点击"振动"按钮后:顶部大字仍是"响铃"(未切换成功),状态卡显示 setRingerMode(vibrate) → false(可能需要授权或当前设备不支持),事件流日志新增 [19:37:43] setRingerMode(vibrate) → false。详见 §5 Q1
4.3 完整示例代码
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:ringer_mode/ringer_mode.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'ringer_mode · OpenHarmony',
theme: ThemeData(colorSchemeSeed: const Color(0xFFDB6D28)),
home: const RingerPage(),
);
}
}
class RingerPage extends StatefulWidget {
const RingerPage({super.key});
State<RingerPage> createState() => _RingerPageState();
}
class _RingerPageState extends State<RingerPage> {
RingerMode _mode = RingerMode.normal;
final List<String> _events = [];
StreamSubscription<RingerMode>? _sub;
String _lastSetResult = '';
void initState() {
super.initState();
RingerModeService.getRingerMode().then((m) => setState(() => _mode = m));
_sub = RingerModeService.ringerModeStream.listen((m) {
_log('ringerModeStream → ${m.name}');
setState(() => _mode = m);
});
}
void dispose() {
_sub?.cancel();
super.dispose();
}
void _log(String msg) {
final now = DateTime.now();
_events.insert(
0,
'[${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}] $msg');
if (_events.length > 20) _events.removeLast();
}
Future<void> _set(RingerMode mode) async {
final ok = await RingerModeService.setRingerMode(mode);
if (!mounted) return;
setState(() => _lastSetResult =
'setRingerMode(${mode.name}) → $ok${ok ? '' : '(可能需要授权或当前设备不支持)'}');
_log('setRingerMode(${mode.name}) → $ok');
}
// 省略 build:与 §4.2 三张图展示的 UI 一致(顶部大字 + 三按钮 + 事件流卡 + 刷新按钮)
// 完整工程见 https://atomgit.com/weixin_52908342/ringer_mode/example
}
签名与构建(活动硬性要求 signingConfig: "default"):
flutter create --platforms ohos .
# 在 example/ohos/build-profile.json5 的 app.signingConfigs 段填入签名材料
flutter build hap --debug
hdc install example/build/ohos/hap/entry-default-signed.hap
hdc shell aa start -b <bundleName> -a EntryAbility
模拟器上验证 ringerModeStream:
- 运行 demo 看到当前模式(响铃/vibrate/silent)
- 回到桌面,打开设置 → 声音和振动 → 在"静音/振动/响铃"间切换
- 回到 demo:顶部大字实时变化 + 事件流日志新增推送
五、FAQ:适配过程与使用问题
Q1:setRingerMode 调用返回 false
真实原因:setRingerMode 宿主接口 AudioRingManager 在 SDK 中已 @deprecated since 9 并整体移除。现行 AudioVolumeGroupManager 仅提供 getRingerMode / on('ringerModeChange') / off,没有 set 方法。
处理方案:
- 应用层引导用户去系统设置修改(推荐):
startAbility跳到设置 app 的声音和振动页(鸿蒙可通过Wantwithaction: 'ohos.settings.sound',或简单context.startAbility({ bundleName: 'com.huawei.hmos.settings' })) - 直接跳设置首页:
hdc shell aa start -b com.huawei.hmos.settings在 demo 中已演示此跳转路径
Q2:编译报 'audio' has no exported member named 'AudioRingManager'
确认 Q1 说明——SDK 已删除 AudioRingManager 命名空间引用。本适配直接返回 false 而不是尝试调旧 API,避免编译失败和运行时系统拒绝双失败。
Q3:编译报 'Namespace 'autoFillManager' has no exported member'
DevEco SDK 6.1.1 默认 API 24,与 3.44.9 不兼容。升级到 DevEco 26.0.0 Release(SDK API 26)。
Q4:getRingerMode 在某些设备返回 null 怎么兜底?
上游 Dart 端 getRingerMode 在 PlatformException 时返回 RingerMode.normal 兜底——本适配保留这一行为。
Q5:ringerModeStream 在小程序/Activity 切换时如何管理生命周期?
Flutter 侧在 dispose() 中 _sub?.cancel()(demo 已演示)。鸿蒙侧 AudioVolumeGroupManager.off('ringerModeChange') 在插件 onDetachedFromEngine 时调用——已实现。
Q6:Demo 在 iOS 上表现如何?
上游 Dart 端 ringerModeStream 在 iOS 上返回固定 Stream.value(RingerMode.silent)(iOS 无 ringer API)。本适配不影响 iOS 行为,iOS 用户会一直看到 silent——这是上游设计,不是 bug。
Q7:示例工程编译报 image_picker 找不到 ohos 实现
本 demo 不依赖 image_picker(ringer_mode 与附件无关)。fork 主包 example 默认依赖 image_picker,但本次 example 重写时已去掉(参考 example/lib/main.dart)。
Q8:Q&A 怎么提 Issue 和 PR?
仓库地址:https://atomgit.com/weixin_52908342/ringer_mode(本期贡献仓库)。
- 提 Issue(四要素):复现步骤 / 期望行为 / 实际行为 /
flutter --version+hdc shell param get const.product.software.version+hdc shell hilog -t OHOSAbility -x关键日志 - 提 PR:Fork → 建
fix/...分支 → 修改 → push → 在 AtomGit 发 PR,描述附鸿蒙真机/模拟器验证截图
六、其他内容
ringer_mode 1.0.2 鸿蒙适配版开箱即用:两行 git 依赖 + 一个 .listen() 即可查询和监听鸿蒙设备的铃声模式,与 Android 完全一致的 API 与字符串协议。Demo 中真实验证了 getRingerMode() 返回 normal/vibrate/silent 三种值、ringerModeStream 在系统设置切换时实时推送。唯一限制是 setRingerMode 不支持(SDK 已移除 AudioRingManager),应用层应引导用户到系统设置完成切换——这是诚实的已知限制,比强行在编译期 hack 旧 API 安全。
更多推荐




所有评论(0)