音频播放
flutter pub add audioplayers
- 创建播放器对象
AudioPlayer audioPlayer = AudioPlayer(); - 开始播放
audioPlayer.play(AssetSource(path));暂停播放audioPlayer.pause();。 - 监听播放状态
audioPlayer.onPlayerStateChanged.listen((state) {}) - 拖动进度条的时候,要禁用监听播放时间手动刷新进度条的操作。
php
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
enum PlayMode { single, singleLoop, listLoop }
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'getx Demo',
initialRoute: "/",
routes: {"/": (context) => Home()},
);
}
}
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final List<Map<String, String>> musicList = [
{"name": "当你", "artist": "王心凌", "path": "music/王心凌-当你.mp3"},
{"name": "我的楼兰", "artist": "云朵", "path": "music/云朵-我的楼兰.mp3"},
{"name": "我的心太乱", "artist": "周传雄", "path": "music/周传雄-我的心太乱.mp3"},
];
final AudioPlayer audioPlayer = AudioPlayer();
int? _currentIndex;
bool _isPlaying = false;
Duration _position = Duration.zero;
Duration _duration = Duration.zero;
double _volume = 1.0;
bool _isDragging = false;
PlayMode _playMode = PlayMode.listLoop;
@override
void initState() {
super.initState();
audioPlayer.onPlayerStateChanged.listen((state) {
setState(() {
switch (state) {
case PlayerState.stopped:
print("onPlayerStateChanged stopped");
break;
case PlayerState.playing:
print("onPlayerStateChanged playing");
break;
case PlayerState.paused:
print("onPlayerStateChanged paused");
break;
case PlayerState.completed:
print("onPlayerStateChanged completed");
break;
case PlayerState.disposed:
print("onPlayerStateChanged disposed");
break;
}
_isPlaying = state == PlayerState.playing;
if (state == PlayerState.completed) {
switch (_playMode) {
case PlayMode.singleLoop:
if (_currentIndex != null) {
audioPlayer.play(
AssetSource(musicList[_currentIndex!]["path"]!),
);
}
break;
case PlayMode.listLoop:
if (_currentIndex != null) {
int nextIndex = (_currentIndex! + 1) % musicList.length;
_currentIndex = nextIndex;
audioPlayer.play(AssetSource(musicList[nextIndex]["path"]!));
}
break;
case PlayMode.single:
_currentIndex = null;
_isPlaying = false;
_position = Duration.zero;
break;
}
}
});
});
audioPlayer.onPositionChanged.listen((pos) {
if (!_isDragging) {
setState(() {
_position = pos;
});
}
});
audioPlayer.onDurationChanged.listen((dur) {
setState(() {
_duration = dur;
});
});
}
Future<void> playMusic(String path, int index) async {
if (_currentIndex == index && _isPlaying) {
await audioPlayer.pause();
} else if (_currentIndex == index && !_isPlaying) {
await audioPlayer.resume();
} else {
_currentIndex = index;
await audioPlayer.play(AssetSource(path));
}
}
@override
void dispose() {
audioPlayer.dispose();
super.dispose();
}
Widget _buildMusicList() {
return ListView(
children: List.generate(musicList.length, (index) {
final item = musicList[index];
return ListTile(
title: Text(item["name"]!),
subtitle: Text(item["artist"]!),
trailing: GestureDetector(
child: Icon(
_currentIndex == index && _isPlaying
? Icons.pause
: Icons.play_arrow,
),
onTap: () {
playMusic(item["path"]!, index);
},
),
);
}),
);
}
// 这里创建一个当前音乐的进度条,并显示当前播放时间和总的播放时间。
Widget _buildMusicStatus() {
return Container(
height: 140,
color: Colors.red,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Text(
_formatDuration(_position),
style: const TextStyle(color: Colors.white),
),
Expanded(
child: Slider(
value:
(_duration.inSeconds > 0
? _position.inSeconds.toDouble()
: 0.0)
.clamp(
0.0,
_duration.inSeconds.toDouble() > 0
? _duration.inSeconds.toDouble()
: 1.0,
)
.toDouble(),
max: _duration.inSeconds.toDouble() > 0
? _duration.inSeconds.toDouble()
: 1,
onChangeStart: (value) {
_isDragging = true;
},
onChanged: _duration.inSeconds > 0
? (value) {
setState(() {
_position = Duration(seconds: value.toInt());
});
}
: null,
onChangeEnd: (value) {
audioPlayer.seek(Duration(seconds: value.toInt()));
_isDragging = false;
},
),
),
Text(
_formatDuration(_duration),
style: const TextStyle(color: Colors.white),
),
],
),
Row(
children: [
GestureDetector(
onTap: () {
setState(() {
_playMode = PlayMode
.values[(_playMode.index + 1) % PlayMode.values.length];
});
},
child: Row(
children: [
Icon(_playModeIcon(), color: Colors.white, size: 20),
const SizedBox(width: 4),
Text(
_playModeLabel(),
style: const TextStyle(color: Colors.white),
),
],
),
),
const SizedBox(width: 16),
const Icon(Icons.volume_down, color: Colors.white, size: 20),
Expanded(
child: Slider(
value: _volume,
onChanged: (value) {
setState(() {
_volume = value;
});
audioPlayer.setVolume(value);
},
),
),
const Icon(Icons.volume_up, color: Colors.white, size: 20),
],
),
],
),
);
}
IconData _playModeIcon() {
switch (_playMode) {
case PlayMode.single:
return Icons.looks_one;
case PlayMode.singleLoop:
return Icons.repeat_one;
case PlayMode.listLoop:
return Icons.repeat;
}
}
String _playModeLabel() {
switch (_playMode) {
case PlayMode.single:
return '单曲播放';
case PlayMode.singleLoop:
return '单曲循环';
case PlayMode.listLoop:
return '列表循环';
}
}
String _formatDuration(Duration d) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = twoDigits(d.inMinutes.remainder(60));
final seconds = twoDigits(d.inSeconds.remainder(60));
return '$minutes:$seconds';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('play music')),
body: Container(
color: Colors.green,
width: double.infinity,
height: double.infinity,
child: Flex(
direction: Axis.vertical,
children: [
Expanded(child: _buildMusicList()),
_buildMusicStatus(),
],
),
),
);
}
}
使用GETX改造
music_controller.dart
import 'package:audioplayers/audioplayers.dart';
import 'package:get/get.dart';
enum PlayMode { single, singleLoop, listLoop }
class MusicController extends GetxController {
final musicList = <Map<String, String>>[
{"name": "当你", "artist": "王心凌", "path": "music/王心凌-当你.mp3"},
{"name": "我的楼兰", "artist": "云朵", "path": "music/云朵-我的楼兰.mp3"},
{"name": "我的心太乱", "artist": "周传雄", "path": "music/周传雄-我的心太乱.mp3"},
];
final audioPlayer = AudioPlayer();
var currentIndex = Rxn<int>();
var isPlaying = false.obs;
var position = Duration.zero.obs;
var duration = Duration.zero.obs;
var volume = 1.0.obs;
var playMode = PlayMode.listLoop.obs;
bool _isDragging = false;
@override
void onInit() {
super.onInit();
audioPlayer.onPlayerStateChanged.listen((state) {
isPlaying.value = state == PlayerState.playing;
if (state == PlayerState.completed) {
_handleComplete();
}
});
audioPlayer.onPositionChanged.listen((pos) {
if (!_isDragging) {
position.value = pos;
}
});
audioPlayer.onDurationChanged.listen((dur) {
duration.value = dur;
});
}
void _handleComplete() {
switch (playMode.value) {
case PlayMode.singleLoop:
if (currentIndex.value != null) {
audioPlayer.play(AssetSource(musicList[currentIndex.value!]["path"]!));
}
break;
case PlayMode.listLoop:
if (currentIndex.value != null) {
int nextIndex = (currentIndex.value! + 1) % musicList.length;
currentIndex.value = nextIndex;
audioPlayer.play(AssetSource(musicList[nextIndex]["path"]!));
}
break;
case PlayMode.single:
currentIndex.value = null;
isPlaying.value = false;
position.value = Duration.zero;
break;
}
}
void playMusic(int index) {
final path = musicList[index]["path"]!;
if (currentIndex.value == index && isPlaying.value) {
audioPlayer.pause();
} else if (currentIndex.value == index && !isPlaying.value) {
audioPlayer.resume();
} else {
currentIndex.value = index;
audioPlayer.play(AssetSource(path));
}
}
void seek(int seconds) {
audioPlayer.seek(Duration(seconds: seconds));
}
void setDragging(bool dragging) {
_isDragging = dragging;
}
void setVolume(double value) {
volume.value = value;
audioPlayer.setVolume(value);
}
void togglePlayMode() {
playMode.value =
PlayMode.values[(playMode.value.index + 1) % PlayMode.values.length];
}
@override
void onClose() {
audioPlayer.dispose();
super.onClose();
}
}
String formatDuration(Duration d) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = twoDigits(d.inMinutes.remainder(60));
final seconds = twoDigits(d.inSeconds.remainder(60));
return '$minutes:$seconds';
}
main.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getdemo/music_controller.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'getx Demo',
initialRoute: "/",
routes: {"/": (context) => Home()},
);
}
}
class Home extends StatelessWidget {
Home({Key? key}) : super(key: key);
final c = Get.put(MusicController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('play music')),
body: Container(
color: Colors.green,
width: double.infinity,
height: double.infinity,
child: Flex(
direction: Axis.vertical,
children: [
Expanded(child: _buildMusicList()),
_buildMusicStatus(),
],
),
),
);
}
Widget _buildMusicList() {
return ListView(
children: List.generate(c.musicList.length, (index) {
final item = c.musicList[index];
return ListTile(
title: Text(item["name"]!),
subtitle: Text(item["artist"]!),
trailing: GestureDetector(
child: Obx(() => Icon(
c.currentIndex.value == index && c.isPlaying.value
? Icons.pause
: Icons.play_arrow,
)),
onTap: () => c.playMusic(index),
),
);
}),
);
}
Widget _buildMusicStatus() {
return Obx(() => Container(
height: 140,
color: Colors.red,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Text(formatDuration(c.position.value),
style: const TextStyle(color: Colors.white)),
Expanded(
child: Slider(
value: (c.duration.value.inSeconds > 0
? c.position.value.inSeconds.toDouble()
: 0.0)
.clamp(
0.0,
c.duration.value.inSeconds.toDouble() > 0
? c.duration.value.inSeconds.toDouble()
: 1.0,
)
.toDouble(),
max: c.duration.value.inSeconds.toDouble() > 0
? c.duration.value.inSeconds.toDouble()
: 1,
onChangeStart: (value) {
c.setDragging(true);
},
onChanged: c.duration.value.inSeconds > 0
? (value) {
c.position.value =
Duration(seconds: value.toInt());
}
: null,
onChangeEnd: (value) {
c.seek(value.toInt());
c.setDragging(false);
},
),
),
Text(formatDuration(c.duration.value),
style: const TextStyle(color: Colors.white)),
],
),
Row(
children: [
GestureDetector(
onTap: c.togglePlayMode,
child: Row(
children: [
Icon(_playModeIcon(c.playMode.value),
color: Colors.white, size: 20),
const SizedBox(width: 4),
Text(_playModeLabel(c.playMode.value),
style: const TextStyle(color: Colors.white)),
],
),
),
const SizedBox(width: 16),
const Icon(Icons.volume_down, color: Colors.white, size: 20),
Expanded(
child: Slider(
value: c.volume.value,
onChanged: (value) {
c.setVolume(value);
},
),
),
const Icon(Icons.volume_up, color: Colors.white, size: 20),
],
),
],
),
));
}
IconData _playModeIcon(PlayMode mode) {
switch (mode) {
case PlayMode.single:
return Icons.looks_one;
case PlayMode.singleLoop:
return Icons.repeat_one;
case PlayMode.listLoop:
return Icons.repeat;
}
}
String _playModeLabel(PlayMode mode) {
switch (mode) {
case PlayMode.single:
return '单曲播放';
case PlayMode.singleLoop:
return '单曲循环';
case PlayMode.listLoop:
return '列表循环';
}
}
}