|
|
|
|
// music_view_test.dart
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
|
|
|
|
|
import '../common/audio_player_controller.dart';
|
|
|
|
|
import '../common/download_manager.dart';
|
|
|
|
|
import '../common_widget/Song_widegt.dart';
|
|
|
|
|
import '../common_widget/app_data.dart';
|
|
|
|
|
import 'comment_view.dart';
|
|
|
|
|
|
|
|
|
|
class MusicView extends StatefulWidget {
|
|
|
|
|
final List<Song> songList;
|
|
|
|
|
final int initialSongIndex;
|
|
|
|
|
final Function(int index, bool isCollected, bool isLiked)?
|
|
|
|
|
onSongStatusChanged;
|
|
|
|
|
|
|
|
|
|
const MusicView({
|
|
|
|
|
super.key,
|
|
|
|
|
required this.songList,
|
|
|
|
|
required this.initialSongIndex,
|
|
|
|
|
this.onSongStatusChanged,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<MusicView> createState() => _MusicViewState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _MusicViewState extends State<MusicView>
|
|
|
|
|
with SingleTickerProviderStateMixin {
|
|
|
|
|
// late AnimationController _rotationController;
|
|
|
|
|
final AudioPlayerController playerController =
|
|
|
|
|
Get.put(AudioPlayerController());
|
|
|
|
|
final downloadManager = Get.find<DownloadManager>();
|
|
|
|
|
final AppData appData = AppData();
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
playerController.initWithSongs(widget.songList, widget.initialSongIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String formatDuration(Duration duration) {
|
|
|
|
|
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
|
|
|
|
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
|
|
|
|
|
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
|
|
|
|
|
return "$twoDigitMinutes:$twoDigitSeconds";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildProgressSlider() {
|
|
|
|
|
return Obx(() {
|
|
|
|
|
final max = playerController.duration.value.inSeconds.toDouble() == 0
|
|
|
|
|
? 0.1
|
|
|
|
|
: playerController.duration.value.inSeconds.toDouble();
|
|
|
|
|
final current =
|
|
|
|
|
playerController.position.value.inSeconds.toDouble().clamp(0, max);
|
|
|
|
|
|
|
|
|
|
return SliderTheme(
|
|
|
|
|
data: const SliderThemeData(
|
|
|
|
|
trackHeight: 3.0,
|
|
|
|
|
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7.0),
|
|
|
|
|
overlayShape: RoundSliderOverlayShape(overlayRadius: 12.0),
|
|
|
|
|
),
|
|
|
|
|
child: Slider(
|
|
|
|
|
min: 0,
|
|
|
|
|
max: max,
|
|
|
|
|
value: current.toDouble(),
|
|
|
|
|
onChanged: (value) {
|
|
|
|
|
playerController.seekTo(Duration(seconds: value.toInt()));
|
|
|
|
|
},
|
|
|
|
|
activeColor: const Color(0xff429482),
|
|
|
|
|
inactiveColor: const Color(0xffE3F0ED),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildPlayButton() {
|
|
|
|
|
return Obx(() {
|
|
|
|
|
return SizedBox(
|
|
|
|
|
width: 52,
|
|
|
|
|
height: 52,
|
|
|
|
|
child: Center(
|
|
|
|
|
child: playerController.isLoading.value
|
|
|
|
|
? const CircularProgressIndicator(
|
|
|
|
|
valueColor: AlwaysStoppedAnimation<Color>(Color(0xff429482)),
|
|
|
|
|
strokeWidth: 3.0,
|
|
|
|
|
)
|
|
|
|
|
: IconButton(
|
|
|
|
|
padding: EdgeInsets.zero,
|
|
|
|
|
constraints: const BoxConstraints(
|
|
|
|
|
minWidth: 52,
|
|
|
|
|
minHeight: 52,
|
|
|
|
|
maxWidth: 52,
|
|
|
|
|
maxHeight: 52,
|
|
|
|
|
),
|
|
|
|
|
onPressed: playerController.playOrPause,
|
|
|
|
|
icon: !playerController.isPlaying.value
|
|
|
|
|
? Image.asset(
|
|
|
|
|
"assets/img/music_play.png",
|
|
|
|
|
width: 52,
|
|
|
|
|
height: 52,
|
|
|
|
|
)
|
|
|
|
|
: Image.asset(
|
|
|
|
|
"assets/img/music_pause.png",
|
|
|
|
|
width: 52,
|
|
|
|
|
height: 52,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Widget _buildRotatingAlbumCover() {
|
|
|
|
|
// return Obx(() {
|
|
|
|
|
// final currentSong =
|
|
|
|
|
// widget.songList[playerController.currentSongIndex.value];
|
|
|
|
|
// return RotationTransition(
|
|
|
|
|
// turns: _rotationController,
|
|
|
|
|
// child: Stack(
|
|
|
|
|
// alignment: Alignment.center,
|
|
|
|
|
// children: [
|
|
|
|
|
// Positioned(
|
|
|
|
|
// child: ClipRRect(
|
|
|
|
|
// child: Image.network(
|
|
|
|
|
// currentSong.artistPic,
|
|
|
|
|
// width: 225,
|
|
|
|
|
// height: 225,
|
|
|
|
|
// fit: BoxFit.cover,
|
|
|
|
|
// ),
|
|
|
|
|
// ),
|
|
|
|
|
// ),
|
|
|
|
|
// ClipRRect(
|
|
|
|
|
// child: Image.asset(
|
|
|
|
|
// "assets/img/music_Ellipse.png",
|
|
|
|
|
// width: 350,
|
|
|
|
|
// height: 350,
|
|
|
|
|
// fit: BoxFit.cover,
|
|
|
|
|
// ),
|
|
|
|
|
// ),
|
|
|
|
|
// ],
|
|
|
|
|
// ),
|
|
|
|
|
// );
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
Widget _buildRotatingAlbumCover() {
|
|
|
|
|
return Obx(() {
|
|
|
|
|
final currentSong =
|
|
|
|
|
widget.songList[playerController.currentSongIndex.value];
|
|
|
|
|
// 移除 RotationTransition,直接返回静态封面
|
|
|
|
|
return Stack(
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
children: [
|
|
|
|
|
Positioned(
|
|
|
|
|
child: ClipRRect(
|
|
|
|
|
child: Image.network(
|
|
|
|
|
currentSong.artistPic,
|
|
|
|
|
width: 225,
|
|
|
|
|
height: 225,
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
ClipRRect(
|
|
|
|
|
child: Image.asset(
|
|
|
|
|
"assets/img/music_Ellipse.png",
|
|
|
|
|
width: 350,
|
|
|
|
|
height: 350,
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _showPlaylist() {
|
|
|
|
|
showModalBottomSheet(
|
|
|
|
|
context: context,
|
|
|
|
|
shape: const RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(30)),
|
|
|
|
|
),
|
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
|
return Container(
|
|
|
|
|
padding: const EdgeInsets.only(top: 15),
|
|
|
|
|
constraints: BoxConstraints(
|
|
|
|
|
maxHeight: MediaQuery.of(context).size.height * 0.45,
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
|
children: [
|
|
|
|
|
const Center(
|
|
|
|
|
child: Text(
|
|
|
|
|
"播放列表",
|
|
|
|
|
style: TextStyle(fontSize: 20),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Obx(() => ListView.builder(
|
|
|
|
|
itemCount: playerController.musicNames.length,
|
|
|
|
|
itemBuilder: (BuildContext context, int index) {
|
|
|
|
|
final isCurrentlyPlaying =
|
|
|
|
|
playerController.currentSongIndex.value == index;
|
|
|
|
|
return ListTile(
|
|
|
|
|
tileColor: isCurrentlyPlaying
|
|
|
|
|
? const Color(0xffE3F0ED)
|
|
|
|
|
: null,
|
|
|
|
|
title: Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.only(left: 20),
|
|
|
|
|
child: Text(
|
|
|
|
|
playerController.musicNames[index],
|
|
|
|
|
style: const TextStyle(fontSize: 18),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.only(right: 20),
|
|
|
|
|
child: Image.asset(
|
|
|
|
|
"assets/img/songs_run.png",
|
|
|
|
|
width: 25,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
onTap: () {
|
|
|
|
|
playerController.changeSong(index);
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
)),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Container(
|
|
|
|
|
decoration: const BoxDecoration(
|
|
|
|
|
image: DecorationImage(
|
|
|
|
|
image: AssetImage("assets/img/app_bg.png"),
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
child: Scaffold(
|
|
|
|
|
resizeToAvoidBottomInset: false,
|
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
|
body: SingleChildScrollView(
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: const EdgeInsets.only(top: 45, left: 10, right: 10),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () {
|
|
|
|
|
Get.back();
|
|
|
|
|
},
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
"assets/img/back.png",
|
|
|
|
|
width: 25,
|
|
|
|
|
height: 25,
|
|
|
|
|
fit: BoxFit.contain,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () {},
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
"assets/img/music_add.png",
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 30,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Obx(() => IconButton(
|
|
|
|
|
onPressed: downloadManager.isDownloading(
|
|
|
|
|
playerController.ids[playerController
|
|
|
|
|
.currentSongIndex.value]) ||
|
|
|
|
|
downloadManager.isCompleted(
|
|
|
|
|
playerController.ids[playerController
|
|
|
|
|
.currentSongIndex.value])
|
|
|
|
|
? null
|
|
|
|
|
: () async {
|
|
|
|
|
await downloadManager.startDownload(
|
|
|
|
|
song: widget.songList[playerController
|
|
|
|
|
.currentSongIndex.value],
|
|
|
|
|
context: context,
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
icon: Obx(() {
|
|
|
|
|
if (downloadManager.isDownloading(
|
|
|
|
|
playerController.ids[playerController
|
|
|
|
|
.currentSongIndex.value])) {
|
|
|
|
|
return Stack(
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
children: [
|
|
|
|
|
SizedBox(
|
|
|
|
|
width: 32,
|
|
|
|
|
height: 32,
|
|
|
|
|
child: CircularProgressIndicator(
|
|
|
|
|
value: downloadManager.getProgress(
|
|
|
|
|
playerController.ids[
|
|
|
|
|
playerController
|
|
|
|
|
.currentSongIndex.value]),
|
|
|
|
|
backgroundColor: Colors.grey[200],
|
|
|
|
|
valueColor:
|
|
|
|
|
const AlwaysStoppedAnimation<
|
|
|
|
|
Color>(Color(0xff429482)),
|
|
|
|
|
strokeWidth: 3.0,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Text(
|
|
|
|
|
'${(downloadManager.getProgress(playerController.ids[playerController.currentSongIndex.value]) * 100).toInt()}',
|
|
|
|
|
style: const TextStyle(fontSize: 12),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return Image.asset(
|
|
|
|
|
downloadManager.isCompleted(
|
|
|
|
|
playerController.ids[playerController
|
|
|
|
|
.currentSongIndex.value])
|
|
|
|
|
? "assets/img/music_download_completed.png"
|
|
|
|
|
: "assets/img/music_download.png",
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 30,
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
)),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 80),
|
|
|
|
|
Center(child: _buildRotatingAlbumCover()),
|
|
|
|
|
const SizedBox(height: 60),
|
|
|
|
|
Obx(() => Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
|
|
children: [
|
|
|
|
|
Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
playerController.musicName.value,
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Text(
|
|
|
|
|
playerController.artistName.value,
|
|
|
|
|
style: const TextStyle(fontSize: 20),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
playerController.toggleLike();
|
|
|
|
|
final currentIndex =
|
|
|
|
|
playerController.currentSongIndex.value;
|
|
|
|
|
widget.onSongStatusChanged?.call(
|
|
|
|
|
currentIndex,
|
|
|
|
|
playerController.collections[currentIndex],
|
|
|
|
|
playerController.likes[currentIndex],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
playerController.likesStatus.value
|
|
|
|
|
? "assets/img/music_good.png"
|
|
|
|
|
: "assets/img/music_good_un.png",
|
|
|
|
|
width: 29,
|
|
|
|
|
height: 29,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
playerController.toggleCollection();
|
|
|
|
|
final currentIndex =
|
|
|
|
|
playerController.currentSongIndex.value;
|
|
|
|
|
widget.onSongStatusChanged?.call(
|
|
|
|
|
currentIndex,
|
|
|
|
|
playerController.collections[currentIndex],
|
|
|
|
|
playerController.likes[currentIndex],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
playerController.collectionsStatus.value
|
|
|
|
|
? "assets/img/music_star.png"
|
|
|
|
|
: "assets/img/music_star_un.png",
|
|
|
|
|
width: 29,
|
|
|
|
|
height: 29,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () {
|
|
|
|
|
Navigator.push(
|
|
|
|
|
context,
|
|
|
|
|
MaterialPageRoute(
|
|
|
|
|
builder: (context) => CommentView(
|
|
|
|
|
id: playerController.ids[playerController
|
|
|
|
|
.currentSongIndex.value],
|
|
|
|
|
song: playerController.musicName.value,
|
|
|
|
|
singer: playerController.artistName.value,
|
|
|
|
|
cover: widget
|
|
|
|
|
.songList[playerController
|
|
|
|
|
.currentSongIndex.value]
|
|
|
|
|
.artistPic,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
"assets/img/music_commend_un.png",
|
|
|
|
|
width: 29,
|
|
|
|
|
height: 29,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)),
|
|
|
|
|
const SizedBox(height: 80),
|
|
|
|
|
Obx(() => Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
formatDuration(playerController.position.value),
|
|
|
|
|
style: const TextStyle(color: Colors.black),
|
|
|
|
|
),
|
|
|
|
|
Expanded(child: _buildProgressSlider()),
|
|
|
|
|
Text(
|
|
|
|
|
formatDuration(playerController.duration.value),
|
|
|
|
|
style: const TextStyle(color: Colors.black),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)),
|
|
|
|
|
const SizedBox(height: 30),
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () {},
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
"assets/img/music_random.png",
|
|
|
|
|
width: 35,
|
|
|
|
|
height: 35,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: playerController.playPrevious,
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
"assets/img/music_back.png",
|
|
|
|
|
width: 42,
|
|
|
|
|
height: 42,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 10),
|
|
|
|
|
_buildPlayButton(),
|
|
|
|
|
const SizedBox(width: 10),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: playerController.playNext,
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
"assets/img/music_next.png",
|
|
|
|
|
width: 42,
|
|
|
|
|
height: 42,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: _showPlaylist,
|
|
|
|
|
icon: Image.asset(
|
|
|
|
|
"assets/img/music_more.png",
|
|
|
|
|
width: 35,
|
|
|
|
|
height: 35,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|