Flutter-OH 插件volume_controller 在鸿蒙平台的使用指南
在 HarmonyOS 应用开发中,经常需要控制或监听系统媒体音量,例如音乐播放器、视频应用、游戏等场景。是一款支持鸿蒙平台的 Flutter 音量控制插件,提供简洁的 API 实现音量获取、设置、监听和静音功能。需求背景:Flutter 应用在鸿蒙设备上需要与系统音量交互,实现与 Android/iOS 一致的用户体验。插件简介:volume_controller 是跨平台系统音量控制插件,支持
Flutter-OH 插件volume_controller 在鸿蒙平台的使用指南
前言
在 HarmonyOS 应用开发中,经常需要控制或监听系统媒体音量,例如音乐播放器、视频应用、游戏等场景。volume_controller 是一款支持鸿蒙平台的 Flutter 音量控制插件,提供简洁的 API 实现音量获取、设置、监听和静音功能。
需求背景:Flutter 应用在鸿蒙设备上需要与系统音量交互,实现与 Android/iOS 一致的用户体验。
插件简介:volume_controller 是跨平台系统音量控制插件,支持 Android、iOS、macOS、Windows、Linux 和 HarmonyOS。
文章目标:帮助开发者在鸿蒙平台上快速集成 volume_controller,掌握基本用法和常见场景实现。
一、插件简介
1.1 什么是 volume_controller
volume_controller 是一个 Flutter 插件,提供统一的 Dart API 来控制和监听各平台(包括 HarmonyOS)的系统媒体音量。通过 MethodChannel 与原生平台通信,支持获取、设置音量,监听音量变化,以及静音/取消静音。
1.2 核心功能
- getVolume:获取当前系统媒体音量(0.0~1.0)
- setVolume:设置系统媒体音量
- addListener:监听系统音量变化,支持可选获取初始音量
- removeListener:移除音量监听
- isMuted:检查当前是否静音
- setMute:静音或取消静音
1.3 支持的功能(鸿蒙平台)
| 功能 | 鸿蒙支持 | 说明 |
|---|---|---|
| getVolume | ✅ | 获取媒体音量 |
| setVolume | ✅ | 需申请 ACCESS_NOTIFICATION_POLICY 权限 |
| addListener | ✅ | 通过系统 volumeChange 事件实现 |
| removeListener | ✅ | 取消监听 |
| isMuted | ✅ | 基于音量是否为 0 判断 |
| setMute | ✅ | 静音/恢复 |
| showSystemUI | ❌ | 鸿蒙不支持调节时显示系统音量条 |
二、环境准备
2.1 开发环境要求
| 工具 | 版本要求 | 说明 |
|---|---|---|
| Flutter SDK | 3.35.8-ohos-0.0.2+ | 支持 HarmonyOS 的 Flutter 版本 |
| Dart SDK | 3.9.2+ | 随 Flutter SDK 一起安装 |
| DevEco Studio | 6.1.0+ | HarmonyOS 官方 IDE |
| HarmonyOS SDK | 5.1.0(18)+ | HarmonyOS 开发工具包 |
2.2 检查 Flutter 环境
flutter doctor -v
确保输出中包含「HarmonyOS toolchain」且状态为 ✓。
2.3 创建 Flutter 项目
若尚未有项目,可先创建:
flutter create my_volume_app
cd my_volume_app
三、安装配置
3.1 添加依赖
方式一:Git 依赖(推荐)
dependencies:
volume_controller:
git:
url: https://github.com/kurenai7968/volume_controller
ref: master
方式二:本地路径依赖
若已克隆仓库到本地:
dependencies:
volume_controller:
path: ../volume_controller
方式三:pub.dev 依赖
若插件已发布到 pub.dev(当前需确认是否包含 ohos 支持):
dependencies:
volume_controller: ^3.4.2
3.2 安装依赖包
flutter pub get
3.3 验证安装
在 lib/main.dart 中尝试导入:
import 'package:volume_controller/volume_controller.dart';
若无报错,说明安装成功。
四、快速开始
4.1 最简单的使用示例
以下示例展示如何获取当前音量并设置音量:
import 'package:flutter/material.dart';
import 'package:volume_controller/volume_controller.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('音量控制示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder<double>(
future: VolumeController.instance.getVolume(),
builder: (context, snapshot) {
final vol = snapshot.data ?? 0.0;
return Text('当前音量: ${(vol * 100).toStringAsFixed(0)}%');
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
await VolumeController.instance.setVolume(0.5);
},
child: const Text('设置为 50%'),
),
],
),
),
),
);
}
}
4.2 运行应用
连接鸿蒙真机,执行:
flutter run
选择鸿蒙设备后,应用将安装并运行。调节设备音量键,可验证插件是否正常工作(若已实现监听,界面会更新)。
五、详细使用指南
5.1 基本用法
VolumeController 采用单例模式,通过 VolumeController.instance 获取实例:
final controller = VolumeController.instance;
5.2 获取当前音量
// 异步获取,返回 0.0~1.0
double volume = await VolumeController.instance.getVolume();
print('当前音量: $volume'); // 例如 0.75 表示 75%
5.3 设置音量
// 设置为 80%
await VolumeController.instance.setVolume(0.8);
// 静音
await VolumeController.instance.setVolume(0.0);
5.4 监听音量变化
import 'dart:async';
late StreamSubscription<double> _subscription;
void initState() {
super.initState();
_subscription = VolumeController.instance.addListener(
(volume) {
print('音量变化: $volume');
setState(() => _currentVolume = volume);
},
fetchInitialVolume: true, // 是否在监听时立即返回当前音量
);
}
void dispose() {
_subscription.cancel(); // 或调用 VolumeController.instance.removeListener();
super.dispose();
}
5.5 静音与取消静音
// 静音
await VolumeController.instance.setMute(true);
// 取消静音(恢复静音前的音量)
await VolumeController.instance.setMute(false);
// 检查是否静音
bool isMuted = await VolumeController.instance.isMuted();
5.6 错误处理
try {
await VolumeController.instance.setVolume(0.5);
} catch (e) {
print('设置音量失败: $e');
// 可能是权限未授予或设备不支持
}
六、实际应用场景
6.1 音乐播放器音量控制
在音乐播放器中,通常需要 Slider 调节音量,并实时显示当前音量:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:volume_controller/volume_controller.dart';
class MusicPlayerPage extends StatefulWidget {
const MusicPlayerPage({super.key});
State<MusicPlayerPage> createState() => _MusicPlayerPageState();
}
class _MusicPlayerPageState extends State<MusicPlayerPage> {
final _controller = VolumeController.instance;
late StreamSubscription<double> _subscription;
double _volume = 0.5;
void initState() {
super.initState();
_subscription = _controller.addListener((v) {
setState(() => _volume = v);
}, fetchInitialVolume: true);
}
void dispose() {
_subscription.cancel();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.volume_up, size: 48),
const SizedBox(height: 16),
Text('音量: ${(_volume * 100).toStringAsFixed(0)}%'),
Slider(
value: _volume,
onChanged: (v) => _controller.setVolume(v),
),
],
),
);
}
}
关键点说明:
- 使用 addListener 保持 UI 与系统音量同步(用户按音量键时界面会更新)
- dispose 时务必 cancel 订阅,避免内存泄漏
6.2 视频播放器静音按钮
点击按钮切换静音状态:
import 'package:flutter/material.dart';
import 'package:volume_controller/volume_controller.dart';
class VideoPlayerPage extends StatefulWidget {
const VideoPlayerPage({super.key});
State<VideoPlayerPage> createState() => _VideoPlayerPageState();
}
class _VideoPlayerPageState extends State<VideoPlayerPage> {
bool _isMuted = false;
Future<void> _toggleMute() async {
_isMuted = !_isMuted;
await VolumeController.instance.setMute(_isMuted);
setState(() {});
}
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// 视频播放区域
Center(child: Text('视频内容')),
// 静音按钮
Positioned(
right: 16,
bottom: 16,
child: IconButton(
icon: Icon(_isMuted ? Icons.volume_off : Icons.volume_up),
onPressed: _toggleMute,
),
),
],
),
);
}
}
关键点说明:
- setMute(true) 会保存当前音量,setMute(false) 时恢复
- 可根据 isMuted 状态切换图标显示
6.3 设置页音量调节
在设置页面提供音量调节入口:
import 'package:flutter/material.dart';
import 'package:volume_controller/volume_controller.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
double _volume = 0.5;
void initState() {
super.initState();
_loadVolume();
}
Future<void> _loadVolume() async {
final v = await VolumeController.instance.getVolume();
setState(() => _volume = v);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('设置')),
body: ListTile(
title: const Text('媒体音量'),
subtitle: Text('${(_volume * 100).toStringAsFixed(0)}%'),
trailing: SizedBox(
width: 120,
child: Slider(
value: _volume,
onChanged: (v) async {
await VolumeController.instance.setVolume(v);
setState(() => _volume = v);
},
),
),
),
);
}
}
关键点说明:
- 进入页面时通过 getVolume 获取当前值
- Slider 的 onChanged 中调用 setVolume 并更新本地状态
6.4 游戏内音量快捷控制
游戏场景中,根据用户操作快速静音/恢复:
import 'package:flutter/material.dart';
import 'package:volume_controller/volume_controller.dart';
class GamePage extends StatefulWidget {
const GamePage({super.key});
State<GamePage> createState() => _GamePageState();
}
class _GamePageState extends State<GamePage> {
bool _wasMutedBeforePause = false;
void _onGamePaused() async {
_wasMutedBeforePause = await VolumeController.instance.isMuted();
await VolumeController.instance.setMute(true);
}
void _onGameResumed() async {
if (!_wasMutedBeforePause) {
await VolumeController.instance.setMute(false);
}
}
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (_) => _onGamePaused(),
onTapUp: (_) => _onGameResumed(),
child: const Center(child: Text('游戏内容')),
);
}
}
关键点说明:
- 暂停时静音,恢复时根据暂停前状态决定是否取消静音
- 可根据实际游戏逻辑调整调用时机
七、注意事项和最佳实践
7.1 生命周期管理
- 使用 addListener 时,必须在 dispose 中调用
subscription.cancel()或removeListener() - 避免在已 dispose 的 Widget 中继续调用插件方法
7.2 权限配置(鸿蒙)
在 ohos/entry/src/main/module.json5 中声明:
"requestPermissions": [
{"name": "ohos.permission.ACCESS_NOTIFICATION_POLICY"}
]
否则 setVolume、setMute 可能失败。
7.3 异步处理
- getVolume、setVolume、isMuted、setMute 均为异步方法,需使用 await 或 .then()
- 在 initState 中调用异步方法时,注意 setState 的时机,避免在 mount 之前调用
7.4 错误处理最佳实践
Future<void> safeSetVolume(double v) async {
try {
await VolumeController.instance.setVolume(v);
} on PlatformException catch (e) {
if (e.code == 'VOLUME_ERROR') {
debugPrint('音量设置失败: ${e.message}');
// 可提示用户检查权限
}
}
}
7.5 性能考虑
- addListener 会注册系统级事件监听,仅在需要时添加,不用时及时取消
- 避免在 addListener 回调中执行耗时操作,以免阻塞 UI
八、常见问题解答
Q1:鸿蒙上 setVolume 调用后没有效果?
答:检查是否在 module.json5 中申请了 ohos.permission.ACCESS_NOTIFICATION_POLICY 权限,并在系统设置中授予应用相应权限。部分设备可能需要在「设置 > 应用 > 权限」中手动开启。
Q2:addListener 后按音量键,回调不触发?
答:确认已正确调用 addListener,且未在 dispose 前调用 removeListener。鸿蒙端通过 AudioVolumeManager.on('volumeChange') 实现,若插件实现有误可能导致无响应,可参考 README.OpenHarmony_CN.md 检查原生实现。
Q3:showSystemUI 在鸿蒙上有效吗?
答:无效。showSystemUI 仅在 Android 和 iOS 上支持,鸿蒙平台调节音量时不会显示系统音量条,该参数会被忽略。
Q4:如何判断当前是否静音?
答:使用 await VolumeController.instance.isMuted()。在鸿蒙上,通常通过音量是否为 0 判断。
Q5:setMute(false) 后音量是多少?
答:会恢复为调用 setMute(true) 之前的音量。若从未调用过 setMute(true),则恢复为 0.5(50%)。
Q6:可以在多个页面同时 addListener 吗?
答:可以,但每个监听会独立收到回调。注意在各自页面 dispose 时取消自己的订阅,避免重复监听和泄漏。
Q7:模拟器上能测试吗?
答:建议在真机上测试。鸿蒙模拟器对音频 API 的支持可能不完整,真机测试更可靠。
九、完整示例代码
以下为完整的可运行示例,包含音量显示、Slider 调节、静音按钮和监听:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:volume_controller/volume_controller.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '音量控制示例',
home: const VolumeDemoPage(),
);
}
}
class VolumeDemoPage extends StatefulWidget {
const VolumeDemoPage({super.key});
State<VolumeDemoPage> createState() => _VolumeDemoPageState();
}
class _VolumeDemoPageState extends State<VolumeDemoPage> {
final _controller = VolumeController.instance;
late StreamSubscription<double> _subscription;
double _volume = 0;
bool _isMuted = false;
void initState() {
super.initState();
_subscription = _controller.addListener((v) {
setState(() => _volume = v);
}, fetchInitialVolume: true);
_controller.isMuted().then((m) => setState(() => _isMuted = m));
}
void dispose() {
_subscription.cancel();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('音量控制')),
body: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('当前音量: ${(_volume * 100).toStringAsFixed(0)}%',
style: Theme.of(context).textTheme.headlineSmall),
const SizedBox(height: 24),
Slider(
value: _volume,
onChanged: (v) => _controller.setVolume(v),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => _controller.setMute(true),
child: const Text('静音'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () => _controller.setMute(false),
child: const Text('取消静音'),
),
],
),
const SizedBox(height: 8),
Text('静音状态: $_isMuted'),
],
),
),
);
}
}
十、总结
关键要点回顾
- 通过
VolumeController.instance获取单例,API 与 Android/iOS 一致 - 鸿蒙需申请
ACCESS_NOTIFICATION_POLICY权限才能设置音量 - addListener 后务必在 dispose 中 cancel,避免泄漏
- showSystemUI 在鸿蒙上不支持,可忽略
下一步
- 查阅 README.OpenHarmony_CN.md 了解 OHOS 适配细节
- 参考 volume_controller 官方仓库 获取最新版本和示例
- 在真机上完成集成和测试
参考资料
更多推荐


所有评论(0)