高级进阶 Flutter for OpenHarmony三方库适配:flutter_sound——音频播放
场景一:音乐播放器需要播放音频文件场景二:教育应用需要播放课程音频场景三:有声读物应用需要播放音频内容场景四:游戏应用需要播放背景音乐和音效是 Flutter 中功能强大的音频插件!它提供了完整的音频播放功能,支持多种音频格式,提供音频流处理能力,在 OpenHarmony 平台上基于鸿蒙原生音频服务实现。
·

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中
flutter_sound插件的音频播放功能,带你全面掌握在应用中播放音频的完整流程。
🎯 前言:为什么需要音频播放?
在移动应用开发中,音频播放功能是许多应用的核心特性:
场景一:音乐播放器需要播放音频文件
场景二:教育应用需要播放课程音频
场景三:有声读物应用需要播放音频内容
场景四:游戏应用需要播放背景音乐和音效
flutter_sound 是 Flutter 中功能强大的音频插件!它提供了完整的音频播放功能,支持多种音频格式,提供音频流处理能力,在 OpenHarmony 平台上基于鸿蒙原生音频服务实现。
🚀 核心能力一览
| 功能特性 | 详细说明 | OpenHarmony 支持 |
|---|---|---|
| 音频播放 | 播放本地和网络音频 | ✅ |
| 多种格式支持 | 支持 AAC、MP3、WAV 等格式 | ✅ |
| 播放控制 | 暂停、恢复、停止、跳转播放 | ✅ |
| 音量控制 | 调节播放音量 | ✅ |
| 播放速度 | 调节播放速度 | ✅ |
| 进度监听 | 监听播放进度 | ✅ |
支持的功能
| 功能 | 说明 | OpenHarmony 支持 |
|---|---|---|
| openPlayer | 打开播放器 | ✅ |
| closePlayer | 关闭播放器 | ✅ |
| startPlayer | 开始播放音频 | ✅ |
| stopPlayer | 停止播放音频 | ✅ |
| pausePlayer | 暂停播放 | ✅ |
| resumePlayer | 恢复播放 | ✅ |
| seekToPlayer | 跳转到指定位置 | ✅ |
| setVolume | 设置音量 | ✅ |
| setSpeed | 设置播放速度 | ✅ |
⚙️ 环境准备:三步走
第一步:添加依赖
📄 pubspec.yaml:
dependencies:
flutter:
sdk: flutter
# 添加 flutter_sound 依赖(OpenHarmony 适配版本)
flutter_sound:
git:
url: https://atomgit.com/openharmony-sig/flutter_sound.git
path: flutter_sound
执行命令:
flutter pub get
第二步:准备音频文件
将音频文件放置在项目的 assets 目录中,并在 pubspec.yaml 中声明:
flutter:
assets:
- assets/audio/sample.mp3
📸 场景一:简单音频播放
📝 完整代码
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '简单音频播放',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF2196F3)),
useMaterial3: true,
),
home: const SimplePlayerPage(),
);
}
}
class SimplePlayerPage extends StatefulWidget {
const SimplePlayerPage({super.key});
State<SimplePlayerPage> createState() => _SimplePlayerPageState();
}
class _SimplePlayerPageState extends State<SimplePlayerPage> {
FlutterSoundPlayer? _player = FlutterSoundPlayer();
bool _playerIsInited = false;
String _audioPath = 'https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3';
void initState() {
super.initState();
_initPlayer();
}
void dispose() {
_player?.closePlayer();
_player = null;
super.dispose();
}
Future<void> _initPlayer() async {
await _player!.openPlayer();
setState(() {
_playerIsInited = true;
});
}
Future<void> _play() async {
await _player!.startPlayer(
fromURI: _audioPath,
whenFinished: () {
setState(() {});
},
);
setState(() {});
}
Future<void> _stop() async {
await _player!.stopPlayer();
setState(() {});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('简单音频播放'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.music_note, size: 100, color: Colors.blue),
const SizedBox(height: 32),
Text(
_player!.isPlaying ? '播放中...' : '已停止',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 32),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton.icon(
onPressed: _playerIsInited && _player!.isStopped ? _play : null,
icon: const Icon(Icons.play_arrow),
label: const Text('播放'),
),
const SizedBox(width: 16),
ElevatedButton.icon(
onPressed: _playerIsInited && _player!.isPlaying ? _stop : null,
icon: const Icon(Icons.stop),
label: const Text('停止'),
),
],
),
],
),
),
);
}
}
🎯 核心要点
- 初始化播放器:使用
openPlayer()初始化 - 播放音频:使用
startPlayer()播放 - 停止播放:使用
stopPlayer()停止 - 状态管理:通过
isPlaying和isStopped判断状态
🎨 场景二:播放控制(暂停/恢复/跳转)

📝 完整代码
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '播放控制',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF2196F3)),
useMaterial3: true,
),
home: const PlayerControlPage(),
);
}
}
class PlayerControlPage extends StatefulWidget {
const PlayerControlPage({super.key});
State<PlayerControlPage> createState() => _PlayerControlPageState();
}
class _PlayerControlPageState extends State<PlayerControlPage> {
FlutterSoundPlayer? _player = FlutterSoundPlayer();
bool _playerIsInited = false;
Duration _duration = Duration.zero;
Duration _position = Duration.zero;
String _audioPath = 'https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3';
void initState() {
super.initState();
_initPlayer();
}
void dispose() {
_player?.closePlayer();
_player = null;
super.dispose();
}
Future<void> _initPlayer() async {
await _player!.openPlayer();
// 设置进度回调
_player!.setSubscriptionDuration(const Duration(milliseconds: 100));
setState(() {
_playerIsInited = true;
});
}
Future<void> _play() async {
await _player!.startPlayer(
fromURI: _audioPath,
whenFinished: () {
setState(() {
_position = Duration.zero;
});
},
);
// 监听播放进度
_player!.onProgress!.listen((event) {
setState(() {
_position = event.position;
_duration = event.duration;
});
});
setState(() {});
}
Future<void> _pause() async {
await _player!.pausePlayer();
setState(() {});
}
Future<void> _resume() async {
await _player!.resumePlayer();
setState(() {});
}
Future<void> _stop() async {
await _player!.stopPlayer();
setState(() {
_position = Duration.zero;
});
}
Future<void> _seekTo(Duration position) async {
await _player!.seekToPlayer(position);
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = twoDigits(duration.inMinutes.remainder(60));
final seconds = twoDigits(duration.inSeconds.remainder(60));
return '$minutes:$seconds';
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('播放控制'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.music_note, size: 100, color: Colors.blue),
const SizedBox(height: 32),
// 进度条
Column(
children: [
Slider(
value: _position.inMilliseconds.toDouble(),
max: _duration.inMilliseconds.toDouble() > 0
? _duration.inMilliseconds.toDouble()
: 1.0,
onChanged: _player!.isPlaying
? (value) {
_seekTo(Duration(milliseconds: value.toInt()));
}
: null,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(_formatDuration(_position)),
Text(_formatDuration(_duration)),
],
),
],
),
const SizedBox(height: 32),
// 播放状态
Text(
_player!.isPlaying
? '播放中...'
: _player!.isPaused
? '已暂停'
: '已停止',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 32),
// 控制按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
iconSize: 48,
onPressed: _playerIsInited && _player!.isStopped ? _play : null,
icon: const Icon(Icons.play_arrow),
),
IconButton(
iconSize: 48,
onPressed: _playerIsInited && _player!.isPlaying ? _pause : null,
icon: const Icon(Icons.pause),
),
IconButton(
iconSize: 48,
onPressed: _playerIsInited && _player!.isPaused ? _resume : null,
icon: const Icon(Icons.play_arrow),
),
IconButton(
iconSize: 48,
onPressed: _playerIsInited && !_player!.isStopped ? _stop : null,
icon: const Icon(Icons.stop),
),
],
),
],
),
),
);
}
}
🎯 核心要点
- 暂停播放:使用
pausePlayer()暂停 - 恢复播放:使用
resumePlayer()恢复 - 跳转播放:使用
seekToPlayer()跳转到指定位置 - 进度监听:通过
onProgress监听播放进度 - 进度显示:使用 Slider 显示和控制播放进度
📊 场景三:音量和速度控制
📝 完整代码
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '音量和速度控制',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF2196F3)),
useMaterial3: true,
),
home: const VolumeSpeedControlPage(),
);
}
}
class VolumeSpeedControlPage extends StatefulWidget {
const VolumeSpeedControlPage({super.key});
State<VolumeSpeedControlPage> createState() => _VolumeSpeedControlPageState();
}
class _VolumeSpeedControlPageState extends State<VolumeSpeedControlPage> {
FlutterSoundPlayer? _player = FlutterSoundPlayer();
bool _playerIsInited = false;
double _volume = 1.0;
double _speed = 1.0;
String _audioPath = 'https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3';
void initState() {
super.initState();
_initPlayer();
}
void dispose() {
_player?.closePlayer();
_player = null;
super.dispose();
}
Future<void> _initPlayer() async {
await _player!.openPlayer();
setState(() {
_playerIsInited = true;
});
}
Future<void> _play() async {
await _player!.startPlayer(
fromURI: _audioPath,
whenFinished: () {
setState(() {});
},
);
// 设置初始音量和速度
await _player!.setVolume(_volume);
await _player!.setSpeed(_speed);
setState(() {});
}
Future<void> _stop() async {
await _player!.stopPlayer();
setState(() {});
}
Future<void> _setVolume(double volume) async {
await _player!.setVolume(volume);
setState(() {
_volume = volume;
});
}
Future<void> _setSpeed(double speed) async {
await _player!.setSpeed(speed);
setState(() {
_speed = speed;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('音量和速度控制'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.music_note, size: 100, color: Colors.blue),
const SizedBox(height: 32),
// 播放状态
Text(
_player!.isPlaying ? '播放中...' : '已停止',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 32),
// 音量控制
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.volume_up),
const SizedBox(width: 8),
Text(
'音量: ${(_volume * 100).toInt()}%',
style: const TextStyle(fontSize: 18),
),
],
),
Slider(
value: _volume,
min: 0.0,
max: 1.0,
divisions: 10,
label: '${(_volume * 100).toInt()}%',
onChanged: _player!.isPlaying
? (value) => _setVolume(value)
: null,
),
],
),
),
),
const SizedBox(height: 16),
// 速度控制
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.speed),
const SizedBox(width: 8),
Text(
'速度: ${_speed.toStringAsFixed(1)}x',
style: const TextStyle(fontSize: 18),
),
],
),
Slider(
value: _speed,
min: 0.5,
max: 2.0,
divisions: 15,
label: '${_speed.toStringAsFixed(1)}x',
onChanged: _player!.isPlaying
? (value) => _setSpeed(value)
: null,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: _player!.isPlaying
? () => _setSpeed(0.5)
: null,
child: const Text('0.5x'),
),
TextButton(
onPressed: _player!.isPlaying
? () => _setSpeed(1.0)
: null,
child: const Text('1.0x'),
),
TextButton(
onPressed: _player!.isPlaying
? () => _setSpeed(1.5)
: null,
child: const Text('1.5x'),
),
TextButton(
onPressed: _player!.isPlaying
? () => _setSpeed(2.0)
: null,
child: const Text('2.0x'),
),
],
),
],
),
),
),
const SizedBox(height: 32),
// 控制按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton.icon(
onPressed: _playerIsInited && _player!.isStopped ? _play : null,
icon: const Icon(Icons.play_arrow),
label: const Text('播放'),
),
const SizedBox(width: 16),
ElevatedButton.icon(
onPressed: _playerIsInited && _player!.isPlaying ? _stop : null,
icon: const Icon(Icons.stop),
label: const Text('停止'),
),
],
),
],
),
),
);
}
}
🎯 核心要点
- 音量控制:使用
setVolume()设置音量(0.0-1.0) - 速度控制:使用
setSpeed()设置播放速度(0.5-2.0) - 实时调节:在播放过程中可以实时调节音量和速度
- 快捷按钮:提供常用速度的快捷按钮
📚 API 参考
FlutterSoundPlayer 主要方法
| 方法 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| openPlayer() | 打开播放器 | 无 | Future |
| closePlayer() | 关闭播放器 | 无 | Future |
| startPlayer() | 开始播放音频 | fromURI, codec, whenFinished | Future |
| stopPlayer() | 停止播放音频 | 无 | Future |
| pausePlayer() | 暂停播放 | 无 | Future |
| resumePlayer() | 恢复播放 | 无 | Future |
| seekToPlayer() | 跳转到指定位置 | Duration | Future |
| setVolume() | 设置音量 | double (0.0-1.0) | Future |
| setSpeed() | 设置播放速度 | double (0.5-2.0) | Future |
| setSubscriptionDuration() | 设置进度回调间隔 | Duration | Future |
播放器状态属性
| 属性 | 类型 | 说明 |
|---|---|---|
| isPlaying | bool | 是否正在播放 |
| isPaused | bool | 是否已暂停 |
| isStopped | bool | 是否已停止 |
| onProgress | Stream | 播放进度流 |
支持的音频格式
| 格式 | 扩展名 | OpenHarmony 支持 |
|---|---|---|
| AAC ADTS | .aac | ✅ |
| AAC MP4 | .m4a | ✅ |
| MP3 | .mp3 | ✅ |
| WAV | .wav | ✅ |
| OGG Vorbis | .ogg | ✅ |
| OGG Opus | .opus | ✅ |
| FLAC | .flac | ✅ |
| PCM16 | .pcm | ✅ |
| PCM Float32 | .pcm | ✅ |
🎓 最佳实践
1. 资源管理
void dispose() {
// 确保关闭播放器
_player?.closePlayer();
_player = null;
super.dispose();
}
2. 错误处理
Future<void> _play() async {
try {
await _player!.startPlayer(
fromURI: _audioPath,
whenFinished: () {
setState(() {});
},
);
} catch (e) {
print('播放失败: $e');
// 显示错误提示
}
}
3. 状态检查
// 播放前检查状态
if (_player!.isStopped) {
await _play();
}
// 暂停前检查状态
if (_player!.isPlaying) {
await _pause();
}
4. 进度监听
// 设置合理的回调间隔
_player!.setSubscriptionDuration(const Duration(milliseconds: 100));
// 监听进度
_player!.onProgress!.listen((event) {
setState(() {
_position = event.position;
_duration = event.duration;
});
});
5. 网络音频处理
// 使用网络音频时添加加载提示
bool _isLoading = false;
Future<void> _playNetworkAudio() async {
setState(() {
_isLoading = true;
});
try {
await _player!.startPlayer(
fromURI: 'https://example.com/audio.mp3',
whenFinished: () {
setState(() {});
},
);
} finally {
setState(() {
_isLoading = false;
});
}
}
🔧 故障排查
问题 1:无法播放音频
症状:调用 startPlayer() 后没有声音
可能原因:
- 音频文件路径错误
- 音频格式不支持
- 设备音量为 0
解决方案:
// 1. 检查文件路径
print('音频路径: $_audioPath');
// 2. 检查播放器状态
print('播放器状态: ${_player!.isPlaying}');
// 3. 设置音量
await _player!.setVolume(1.0);
问题 2:播放卡顿
症状:播放过程中出现卡顿
可能原因:
- 网络音频加载慢
- 音频文件过大
- 设备性能不足
解决方案:
// 1. 使用本地缓存
// 2. 降低音频质量
// 3. 优化 UI 更新频率
_player!.setSubscriptionDuration(const Duration(milliseconds: 500));
问题 3:进度不更新
症状:播放进度不更新
可能原因:
- 未设置订阅间隔
- 未监听进度流
解决方案:
// 设置订阅间隔
await _player!.setSubscriptionDuration(const Duration(milliseconds: 100));
// 监听进度
_player!.onProgress!.listen((event) {
setState(() {
_position = event.position;
_duration = event.duration;
});
});
问题 4:速度控制无效
症状:调用 setSpeed() 后速度没有变化
可能原因:
- 音频格式不支持变速
- 在播放前设置速度
解决方案:
// 在播放开始后设置速度
await _player!.startPlayer(fromURI: _audioPath);
await Future.delayed(const Duration(milliseconds: 100));
await _player!.setSpeed(1.5);
🔗 相关资源
更多推荐
所有评论(0)