feat: 适配QQ音乐

chen
Spark 8 months ago
parent 10d326f3ff
commit 7f4a2a89cf

@ -54,6 +54,14 @@ android {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64', 'x86' abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64', 'x86'
} }
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.music_player_miao" applicationId "com.example.music_player_miao"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
7d34a1186938d51f8f2b6577f7a6094e f649f7b73ed118947e70d0b9aa9e6a1f

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -8,12 +8,64 @@
"variantName": "debug", "variantName": "debug",
"elements": [ "elements": [
{ {
"type": "SINGLE", "type": "UNIVERSAL",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 1, "versionCode": 1,
"versionName": "1.0.0", "versionName": "1.0.0",
"outputFile": "AndroidManifest.xml" "outputFile": "universal/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "x86_64/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "x86/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "armeabi-v7a/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "arm64-v8a/AndroidManifest.xml"
} }
], ],
"elementType": "File" "elementType": "File"

@ -8,12 +8,64 @@
"variantName": "debug", "variantName": "debug",
"elements": [ "elements": [
{ {
"type": "SINGLE", "type": "UNIVERSAL",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 1, "versionCode": 1,
"versionName": "1.0.0", "versionName": "1.0.0",
"outputFile": "AndroidManifest.xml" "outputFile": "universal/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "x86_64/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "x86/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "armeabi-v7a/AndroidManifest.xml"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "arm64-v8a/AndroidManifest.xml"
} }
], ],
"elementType": "File" "elementType": "File"

@ -8,12 +8,64 @@
"variantName": "debug", "variantName": "debug",
"elements": [ "elements": [
{ {
"type": "SINGLE", "type": "UNIVERSAL",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 1, "versionCode": 1,
"versionName": "1.0.0", "versionName": "1.0.0",
"outputFile": "resources-debug.ap_" "outputFile": "resources-universalDebug.ap_"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "resources-arm64-v8aDebug.ap_"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "resources-x86_64Debug.ap_"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "resources-x86Debug.ap_"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "resources-armeabi-v7aDebug.ap_"
} }
], ],
"elementType": "File" "elementType": "File"

@ -8,12 +8,64 @@
"variantName": "debug", "variantName": "debug",
"elements": [ "elements": [
{ {
"type": "SINGLE", "type": "UNIVERSAL",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 1, "versionCode": 1,
"versionName": "1.0.0", "versionName": "1.0.0",
"outputFile": "app-debug.apk" "outputFile": "app-universal-debug.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "app-arm64-v8a-debug.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "app-x86_64-debug.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "app-x86-debug.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "app-armeabi-v7a-debug.apk"
} }
], ],
"elementType": "File" "elementType": "File"

File diff suppressed because it is too large Load Diff

@ -837,7 +837,7 @@ library {
} }
library { library {
digests { digests {
sha256: "\342\346o\361\313\365\357\032\221\022+`{\253\254\031\344\275\2603%p\371\264\230%\323\3062\377f\367" sha256: "\263\345qt\2246\203g\vTm\025vRp\272\311\347\363]\226\211\220\255.\354\354+\v^\322\323"
} }
} }
library_dependencies { library_dependencies {

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -26,6 +26,7 @@ class GetMusic {
'Authorization': Authorization, 'Authorization': Authorization,
'Content-Type': 'application/json;charset=UTF-8' 'Content-Type': 'application/json;charset=UTF-8'
})); }));
print(response.data);
return MusicsListBean.fromMap(response.data); return MusicsListBean.fromMap(response.data);
} }

@ -0,0 +1,20 @@
import 'package:dio/dio.dart';
import 'package:music_player_miao/models/universal_bean.dart';
const String _qqmusicUrl = 'https://qqmusic.aspark.cc/musicUrl';
class QQMusicApiClient {
final Dio dio = Dio();
Future<UniversalBean> get({
required String mid,
}) async {
Response response = await dio.get(
'$_qqmusicUrl?mid=$mid',
options: Options(headers:{'Content-Type':'application/json;charset=UTF-8'})
);
print(mid);
print(response.data);
return UniversalBean.formMap(response.data);
}
}

@ -4,6 +4,7 @@ import 'package:music_player_miao/models/search_bean.dart';
import 'package:music_player_miao/models/songlist_bean.dart'; import 'package:music_player_miao/models/songlist_bean.dart';
import '../models/getAllSongs_bean.dart'; import '../models/getAllSongs_bean.dart';
import '../models/getLikeList_bean.dart';
import '../models/universal_bean.dart'; import '../models/universal_bean.dart';
import 'package:music_player_miao/models/getMyWorks_bean.dart'; import 'package:music_player_miao/models/getMyWorks_bean.dart';
const String _SonglistURL = 'http://8.210.250.29:10010/songlists'; const String _SonglistURL = 'http://8.210.250.29:10010/songlists';
@ -15,7 +16,7 @@ class SonglistApi {
final Dio dio = Dio(); final Dio dio = Dio();
/// ///
Future<MyLikes> getMyCollection({required String Authorization}) async { Future<LikeListBean> getMyCollection({required String Authorization}) async {
Response response = await dio.get(_MyCollectionURL, Response response = await dio.get(_MyCollectionURL,
data: { data: {
'Authorization': Authorization, 'Authorization': Authorization,
@ -25,7 +26,7 @@ class SonglistApi {
'Content-Type': 'application/json;charset=UTF-8' 'Content-Type': 'application/json;charset=UTF-8'
})); }));
print(response.data); print(response.data);
return MyLikes.formMap(response.data); return LikeListBean.formMap(response.data);
} }
/// ///
Future<MyMusicListBean> getAllSongs({required int id, required String Authorization}) async { Future<MyMusicListBean> getAllSongs({required int id, required String Authorization}) async {

@ -4,6 +4,7 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import 'package:music_player_miao/api/api_qqmusic.dart';
import '../api/api_collection.dart'; import '../api/api_collection.dart';
import '../api/api_music_likes.dart'; import '../api/api_music_likes.dart';
import '../common_widget/Song_widegt.dart'; import '../common_widget/Song_widegt.dart';
@ -14,13 +15,14 @@ import '../api/api_music_list.dart';
import '../models/universal_bean.dart'; import '../models/universal_bean.dart';
enum PlayMode { enum PlayMode {
sequence, // sequence, //
random, // random, //
single // single //
} }
class AudioPlayerController extends GetxController { class AudioPlayerController extends GetxController {
static AudioPlayerController? _instance; static AudioPlayerController? _instance;
static AudioPlayerController get instance { static AudioPlayerController get instance {
_instance ??= AudioPlayerController._(); _instance ??= AudioPlayerController._();
return _instance!; return _instance!;
@ -164,7 +166,8 @@ class AudioPlayerController extends GetxController {
final currentIndex = currentSongIndex.value; final currentIndex = currentSongIndex.value;
likesStatus.value = !likesStatus.value; likesStatus.value = !likesStatus.value;
likes[currentIndex] = likesStatus.value; likes[currentIndex] = likesStatus.value;
UniversalBean response = await LikesApiMusic().likesMusic(musicId: ids[currentIndex], Authorization: AppData().currentToken); UniversalBean response = await LikesApiMusic().likesMusic(
musicId: ids[currentIndex], Authorization: AppData().currentToken);
if (response.code != 200) { if (response.code != 200) {
likesStatus.value = !likesStatus.value; likesStatus.value = !likesStatus.value;
likes[currentIndex] = likesStatus.value; likes[currentIndex] = likesStatus.value;
@ -175,7 +178,8 @@ class AudioPlayerController extends GetxController {
final currentIndex = currentSongIndex.value; final currentIndex = currentSongIndex.value;
collectionsStatus.value = !collectionsStatus.value; collectionsStatus.value = !collectionsStatus.value;
collections[currentIndex] = collectionsStatus.value; collections[currentIndex] = collectionsStatus.value;
UniversalBean response = await CollectionApiMusic().addCollection(musicId: ids[currentIndex], Authorization: AppData().currentToken); UniversalBean response = await CollectionApiMusic().addCollection(
musicId: ids[currentIndex], Authorization: AppData().currentToken);
if (response.code != 200) { if (response.code != 200) {
collectionsStatus.value = !collectionsStatus.value; collectionsStatus.value = !collectionsStatus.value;
collections[currentIndex] = collectionsStatus.value; collections[currentIndex] = collectionsStatus.value;
@ -197,10 +201,12 @@ class AudioPlayerController extends GetxController {
await _checkAndUpdateSongStatus(currentSongIndex.value); await _checkAndUpdateSongStatus(currentSongIndex.value);
try { try {
final localSong = downloadManager.getLocalSong(songList[currentSongIndex.value].id); final localSong =
downloadManager.getLocalSong(songList[currentSongIndex.value].id);
if (localSong == null && songUrls[currentSongIndex.value] == '') { if (localSong == null && songList[currentSongIndex.value].mid != null) {
// TODO final response = await QQMusicApiClient().get(mid: songList[currentSongIndex.value].mid!);
songUrls[currentSongIndex.value] = response.data!;
} }
final audioSource = localSong != null final audioSource = localSong != null

@ -50,6 +50,7 @@ class DownloadItem {
id: json['song']['id'], id: json['song']['id'],
likes: json['song']['likes'], likes: json['song']['likes'],
collection: json['song']['collection'], collection: json['song']['collection'],
mid: json['song']['mid'],
), ),
progress: json['progress'], progress: json['progress'],
isCompleted: json['isCompleted'], isCompleted: json['isCompleted'],
@ -161,7 +162,9 @@ class DownloadManager extends GetxController {
musicurl: song.musicurl, musicurl: song.musicurl,
id: song.id, id: song.id,
likes: song.likes, likes: song.likes,
collection: song.collection); collection: song.collection,
mid: song.mid,
);
final downloadItem = DownloadItem( final downloadItem = DownloadItem(
song: songCopy, song: songCopy,

@ -7,6 +7,7 @@ class Song {
int id; int id;
bool? likes; bool? likes;
bool? collection; bool? collection;
String? mid;
// //
Song({ Song({
@ -18,19 +19,21 @@ class Song {
required this.id, required this.id,
required this.likes, required this.likes,
required this.collection, required this.collection,
required this.mid,
}); });
// 使 Map Song // 使 Map Song
factory Song.fromMap(Map<String, dynamic> map) { factory Song.fromMap(Map<String, dynamic> map) {
return Song( return Song(
pic: map['coverPath'] ?? '', // coverPath pic: map['coverPath'] ?? 'https://api.aspark.cc/image/1/6759856d288fd.jpg', // coverPath
artistPic: map['coverPath'] ?? '', // artistPic artistPic: map['coverPath'] ?? 'https://api.aspark.cc/image/1/6759856d288fd.jpg', // artistPic
title: map['name'] ?? '', // name title: map['name'] ?? '', // name
artist: map['singerName'] ?? '', // singerName artist: map['singerName'] ?? '', // singerName
musicurl: map['musicPath'] ?? '', // musicPath musicurl: map['musicPath'] ?? '', // musicPath
id: map['id'] ?? 0, // ID id id: map['id'] ?? 0, // ID id
likes: map['likeOrNot'] ?? false, // likeOrNot likes: map['likeOrNot'] ?? false, // likeOrNot
collection: map['collectOrNot'] ?? false, // collectOrNot collection: map['collectOrNot'] ?? false, // collectOrNot
mid: map['mid'] ?? '', // mid mid
); );
} }
} }

@ -21,15 +21,17 @@ class MusicItem {
String? uploadUserName; String? uploadUserName;
bool? likeOrNot; bool? likeOrNot;
bool? collectOrNot; bool? collectOrNot;
String? mid;
MusicItem.fromMap(Map map) { MusicItem.fromMap(Map map) {
id = map['id']; id = map['id'];
name = map['name']; name = map['name'];
coverPath = map['coverPath']; coverPath = map['coverPath'] == '' ? 'https://api.aspark.cc/image/1/6759856d288fd.jpg' : map['coverPath'];
musicPath = map['musicPath']; musicPath = map['musicPath'];
singerName = map['singerName']; singerName = map['singerName'];
uploadUserName = map['uploadUserName']; uploadUserName = map['uploadUserName'] ?? '';
likeOrNot = map['likeOrNot']; likeOrNot = map['likeOrNot'];
collectOrNot = map['collectOrNot']; collectOrNot = map['collectOrNot'];
mid = map['mid'] ?? '';
} }
} }

@ -23,16 +23,18 @@ class LikeListData {
String? uploadUserName; String? uploadUserName;
bool? likes; bool? likes;
bool? collection; bool? collection;
String? mid;
LikeListData._formMap(Map map) { LikeListData._formMap(Map map) {
id = map['id']; id = map['id'];
name = map['name']; name = map['name'];
coverPath = map['coverPath']; coverPath = map['coverPath'];
musicPath = map['musicPath']; musicPath = map['musicPath'] ?? '';
singerName = map['singerName']; singerName = map['singerName'];
uploadUserName = map['uploadUserName']; uploadUserName = map['uploadUserName'];
likes = map['likes']; likes = map['likes'];
collection = map['collection']; collection = map['collection'];
mid = map['mid'] ?? '';
} }
// Map, // Map,
@ -46,6 +48,7 @@ class LikeListData {
'uploadUserName': uploadUserName, 'uploadUserName': uploadUserName,
'likes': likes, 'likes': likes,
'collection': collection, 'collection': collection,
'mid': mid,
}; };
} }
} }

@ -24,6 +24,7 @@ class DataBean {
String? musicPath; String? musicPath;
String? singerName; String? singerName;
String? uploadUserName; String? uploadUserName;
String? mid;
DataBean._formMap(Map map) { DataBean._formMap(Map map) {
id = map['id']; id = map['id'];
@ -32,5 +33,6 @@ class DataBean {
musicPath = map['musicPath']; musicPath = map['musicPath'];
singerName = map['singerName']; singerName = map['singerName'];
uploadUserName = map['uploadUserName']; uploadUserName = map['uploadUserName'];
mid = map['mid'] ?? '';
} }
} }

@ -23,6 +23,7 @@ class DataBean {
String? coverPath; String? coverPath;
String? musicPath; String? musicPath;
String? name; String? name;
String? mid;
DataBean._formMap(Map map) { DataBean._formMap(Map map) {
id = map['id']; id = map['id'];
@ -30,5 +31,6 @@ class DataBean {
coverPath = map['coverPath']; coverPath = map['coverPath'];
musicPath = map['musicPath']; musicPath = map['musicPath'];
name = map['name']; name = map['name'];
mid = map['mid'];
} }
} }

@ -1,9 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_swiper_view/flutter_swiper_view.dart'; import 'package:flutter_swiper_view/flutter_swiper_view.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:music_player_miao/api/api_music_return.dart';
import 'package:music_player_miao/common_widget/app_data.dart'; import 'package:music_player_miao/common_widget/app_data.dart';
import 'package:music_player_miao/models/search_bean.dart';
import 'package:music_player_miao/view/comment_view.dart'; import 'package:music_player_miao/view/comment_view.dart';
import 'package:music_player_miao/view/search_view.dart'; import 'package:music_player_miao/view/search_view.dart';
import '../../view_model/home_view_model.dart'; import '../../view_model/home_view_model.dart';
@ -11,14 +9,11 @@ import '../api/api_music_likes.dart';
import '../api/api_music_list.dart'; import '../api/api_music_list.dart';
import '../common/download_manager.dart'; import '../common/download_manager.dart';
import '../common_widget/Song_widegt.dart'; import '../common_widget/Song_widegt.dart';
import '../common_widget/list_cell.dart';
import '../models/MusicsListBean.dart'; import '../models/MusicsListBean.dart';
import '../models/getMusicList_bean.dart';
import '../models/universal_bean.dart'; import '../models/universal_bean.dart';
import 'music_view.dart'; import 'music_view.dart';
import '../api/api_collection.dart'; import '../api/api_collection.dart';
import '../api/api_music_likes.dart';
import '../models/universal_bean.dart';
class HomeView extends StatefulWidget { class HomeView extends StatefulWidget {
const HomeView({super.key}); const HomeView({super.key});
@ -30,9 +25,6 @@ class HomeView extends StatefulWidget {
class _HomeViewState extends State<HomeView> class _HomeViewState extends State<HomeView>
with AutomaticKeepAliveClientMixin { with AutomaticKeepAliveClientMixin {
final homeVM = Get.put(HomeViewModel()); final homeVM = Get.put(HomeViewModel());
// final TextEditingController _controller = TextEditingController();
bool _isSearching = false;
final downloadManager = Get.put(DownloadManager()); final downloadManager = Get.put(DownloadManager());
List<Song> selectedSongs = []; List<Song> selectedSongs = [];
@ -59,6 +51,7 @@ class _HomeViewState extends State<HomeView>
try { try {
MusicsListBean bean = await GetMusic() MusicsListBean bean = await GetMusic()
.getMusicList(Authorization: AppData().currentToken, num: 10); .getMusicList(Authorization: AppData().currentToken, num: 10);
setState(() { setState(() {
selectedSongs = []; selectedSongs = [];
for (var data in bean.data!) { for (var data in bean.data!) {
@ -66,14 +59,17 @@ class _HomeViewState extends State<HomeView>
artistPic: data.coverPath!, artistPic: data.coverPath!,
title: data.name!, title: data.name!,
artist: data.singerName!, artist: data.singerName!,
musicurl: data.musicPath!, musicurl: data.musicPath,
pic: data.coverPath!, pic: data.coverPath!,
id: data.id!, id: data.id!,
likes: data.likeOrNot!, likes: data.likeOrNot!,
collection: data.collectOrNot!, collection: data.collectOrNot!,
mid: data.mid,
)); ));
print(data.coverPath!);
} }
}); });
} catch (e) { } catch (e) {
print('Error occurred while fetching song list: $e'); print('Error occurred while fetching song list: $e');
} }
@ -86,101 +82,6 @@ class _HomeViewState extends State<HomeView>
{"image": "assets/img/banner2.png"}, {"image": "assets/img/banner2.png"},
]; ];
List<Song> _filteredData = [];
Future<void> _filterData(String query) async {
if (query.isNotEmpty) {
try {
//
SearchBean bean = await SearchMusic().search(
keyword: query,
Authorization: AppData().currentToken,
);
//
if (bean.code == 200 && bean.data != null) {
// <EFBFBD><EFBFBD><EFBFBD>
List<Future<Song?>> songDetailsFutures = [];
// id
for (var data in bean.data!) {
if (data.id != null) {
// id null
// 使 id Future
songDetailsFutures.add(GetMusicDetail()
.getMusicDetail(
songId: data.id!,
Authorization: AppData().currentToken,
)
.then((details) {
if (details != null) {
// Song
return Song(
artistPic: details.artistPic ?? '',
//
title: data.name ?? '',
//
artist: details.artist ?? '',
//
musicurl: details.musicurl ?? '',
//
pic: details.pic ?? '',
//
id: details.id,
// ID
likes: details.likes,
//
collection: details.collection, //
);
}
return null; // null
}).catchError((error) {
print("Error occurred while fetching song details: $error");
return null; // null
}));
} else {
print("Song ID is null for song: ${data.name}");
}
}
// 使 Future.wait
List<Song?> songDetailsList = await Future.wait(songDetailsFutures);
// null
List<Song> validSongDetails = songDetailsList
.where((song) => song != null)
.cast<Song>()
.toList();
// UI _filteredData
setState(() {
_filteredData = validSongDetails; //
_isSearching = true; //
});
//
print("Filtered Data: $_filteredData");
} else {
setState(() {
_filteredData = [];
_isSearching = false;
});
}
} catch (error) {
print("Error occurred during search: $error");
setState(() {
_filteredData = [];
_isSearching = false;
});
}
} else {
setState(() {
_filteredData = [];
_isSearching = false;
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
@ -494,7 +395,7 @@ class _HomeViewState extends State<HomeView>
), ),
), ),
const SizedBox( const SizedBox(
height: 110, height: 130,
), ),
], ],
), ),

@ -1,7 +1,6 @@
// music_view.dart // music_view.dart
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:music_player_miao/common_widget/app_data.dart'; import 'package:music_player_miao/common_widget/app_data.dart';
@ -14,8 +13,6 @@ import '../common/songlist_bottom_sheet.dart';
import '../common_widget/Song_widegt.dart'; import '../common_widget/Song_widegt.dart';
import 'comment_view.dart'; import 'comment_view.dart';
import 'dart:async';
import 'package:flutter/material.dart';
class ScrollingText extends StatefulWidget { class ScrollingText extends StatefulWidget {
final String text; final String text;
@ -338,7 +335,12 @@ class _MusicViewState extends State<MusicView>
color: Colors.grey[200], color: Colors.grey[200],
borderRadius: BorderRadius.circular(112.5), borderRadius: BorderRadius.circular(112.5),
), ),
child: const Icon(Icons.error_outline, size: 40), child: Image.asset(
"assets/img/error.jpg",
width: 225,
height: 225,
fit: BoxFit.cover,
),
); );
}, },
), ),
@ -587,6 +589,7 @@ class _MusicViewState extends State<MusicView>
SnackBar( SnackBar(
content: Text(response.msg!), content: Text(response.msg!),
duration: const Duration(milliseconds: 1500), duration: const Duration(milliseconds: 1500),
behavior: SnackBarBehavior.floating,
), ),
); );
} }
@ -597,6 +600,7 @@ class _MusicViewState extends State<MusicView>
SnackBar( SnackBar(
content: Text(e.toString()), content: Text(e.toString()),
duration: const Duration(milliseconds: 1500), duration: const Duration(milliseconds: 1500),
behavior: SnackBarBehavior.floating,
), ),
); );
} }
@ -615,18 +619,22 @@ class _MusicViewState extends State<MusicView>
final currentId = audioController final currentId = audioController
.ids[audioController.currentSongIndex.value]; .ids[audioController.currentSongIndex.value];
return IconButton( return IconButton(
onPressed: onPressed: downloadManager
downloadManager.isDownloading(currentId) || .isDownloading(currentId) ||
downloadManager.isCompleted(currentId) downloadManager.isCompleted(currentId)
? null ? null
: () async { : () async {
await downloadManager.startDownload( Song s = widget.songList[
song: widget.songList[audioController audioController.currentSongIndex.value];
.currentSongIndex.value], s.musicurl = audioController.songUrls[
context: context, audioController.currentSongIndex.value];
); await downloadManager.startDownload(
Get.find<DownloadCountController>().refreshCount(downloadManager); song: s,
}, context: context,
);
Get.find<DownloadCountController>()
.refreshCount(downloadManager);
},
icon: downloadManager.isDownloading(currentId) icon: downloadManager.isDownloading(currentId)
? Stack( ? Stack(
alignment: Alignment.center, alignment: Alignment.center,
@ -887,7 +895,7 @@ class _MusicViewState extends State<MusicView>
audioController.playNext(manual: true); audioController.playNext(manual: true);
_rotationController.reset(); _rotationController.reset();
}, },
icon:ColorFiltered( icon: ColorFiltered(
colorFilter: ColorFilter.mode( colorFilter: ColorFilter.mode(
Colors.grey[700]!, Colors.grey[700]!,
BlendMode.srcIn, BlendMode.srcIn,

@ -1,190 +1,65 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:music_player_miao/common_widget/Song_widegt.dart'; import 'package:music_player_miao/api/api_music_likes_list.dart';
import '../../api/api_songlist.dart'; import 'package:music_player_miao/common_widget/app_data.dart';
import '../../common_widget/app_data.dart'; import 'package:music_player_miao/models/getLikeList_bean.dart';
import '../../models/getMyWorks_bean.dart'; import 'package:music_player_miao/view_model/home_view_model.dart';
import '../../view_model/home_view_model.dart'; import '../../common_widget/Song_widegt.dart';
import '../models/getMyLike_bean.dart'; import '../../common/download_manager.dart';
import '../api/api_songlist.dart';
import 'music_view.dart';
///
class MyCollectionView extends StatefulWidget { class MyCollectionView extends StatefulWidget {
const MyCollectionView({super.key}); const MyCollectionView({Key? key}) : super(key: key);
@override @override
State<MyCollectionView> createState() => _MyCollectionViewState(); State<MyCollectionView> createState() => _MyCollectionViewState();
} }
class _MyCollectionViewState extends State<MyCollectionView> { class _MyCollectionViewState extends State<MyCollectionView> {
int MyWorkCount = 0; //
List MyWorkNames = []; List<LikeListData> likedSongs = [];
List Songid = []; //
List coverPath = [];
List musicPath = [];
List singerName = [];
List uploaderUserName = [];
final listVM = Get.put(HomeViewModel());
bool _isSelectMode = false; bool _isSelectMode = false;
bool _isSelectListMode = false; //
List<bool> _mySongListSelections = List.generate(2, (index) => false); List<bool> _selectedItems = [];
List<bool> _selectedItems = List.generate(100, (index) => false); //
final listVM = Get.put(HomeViewModel());
final downloadManager = Get.put(DownloadManager());
//
@override @override
void initState() { void initState() {
super.initState(); super.initState();
print('初始化正常'); //
_fetchMyLikesData(); _fetchLikedSongs();
} }
/// ///
Future<void> _fetchMyLikesData() async { Future<void> _fetchLikedSongs() async {
try { try {
MyLikes bean2 = await SonglistApi().getMyCollection( // API
LikeListBean response = await SonglistApi().getMyCollection(
Authorization: AppData().currentToken, Authorization: AppData().currentToken,
); );
setState(() { //
print("bean2: $bean2"); if (response.code == 200 && response.data != null) {
print('bean2.data: ${bean2.data}');
MyWorkNames = bean2.data!.map((data) => data.name!).toList();
Songid = bean2.data!.map((data) => data.id!).toList();
coverPath = bean2.data!.map((data) => data.coverPath!).toList();
musicPath = bean2.data!.map((data) => data.musicPath!).toList();
singerName = bean2.data!.map((data) => data.singerName!).toList();
uploaderUserName = bean2.data!.map((data) => data.uploadUserName!).toList();
print('赋值开始');
MyWorkCount = MyWorkNames.length;
print('赋值结束');
});
} catch (error) {
print('Error fetching myLikes data: $error');
}
}
//
void _toggleSelectMode() {
setState(() {
_isSelectMode = !_isSelectMode;
_isSelectListMode = !_isSelectListMode;
if (!_isSelectMode) {
_selectedItems = List.generate(10, (index) => false);
}
if (!_isSelectListMode) {
_mySongListSelections = List.generate(2, (index) => false);
}
});
}
void _selectAll() {
setState(() {
_selectedItems = List.generate(10, (index) => true);
});
}
void _showSelectionDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius:BorderRadius.circular(10),
),
title: Row(
children: [
const Text("添加到",),
Text(
'(${_selectedItems.where((item) => item).length} 首)',
style: const TextStyle(
color: Color(0xff429482),
fontSize: 16
),
)
],
),
content:SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
for (int i = 0; i < _mySongListSelections.length; i++)
_buildSongListTile(i),
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
style: TextButton.styleFrom(
backgroundColor: const Color(0xff429482),
minimumSize: const Size(130, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
child: const Text(
"取消",
style: TextStyle(color: Colors.white),
),
),
TextButton(
onPressed: () {
},
style: TextButton.styleFrom(
backgroundColor: const Color(0xff429482),
minimumSize: const Size(130, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
child: const Text(
"保存",
style: TextStyle(color: Colors.white),
),
),
],
);
},
);
}
Widget _buildSongListTile(int index) {
return ListTile(
title: Text("我的歌单 $index"),
trailing:
Checkbox(
value: _mySongListSelections[index],
onChanged: (value) {
setState(() {
_mySongListSelections[index] = value ?? false;
});
},
shape: const CircleBorder(),
activeColor: const Color(0xff429482),
),
// Checkbox(
// value: _mySongListSelections[index],
// onChanged: (value) {
// setState(() {
// _mySongListSelections[index] = value!;
// });
// },
// shape: CircleBorder(),
// activeColor: Color(0xff429482),
// ),
onTap: () {
setState(() { setState(() {
_mySongListSelections[index] = !_mySongListSelections[index]; likedSongs = response.data!;
//
_selectedItems = List.generate(likedSongs.length, (index) => false);
}); });
}, }
); } catch (error) {
print('Error fetching liked songs: $error');
}
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
//
decoration: const BoxDecoration( decoration: const BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage("assets/img/app_bg.png"), image: AssetImage("assets/img/app_bg.png"),
@ -193,14 +68,16 @@ class _MyCollectionViewState extends State<MyCollectionView> {
), ),
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
//
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
centerTitle: true, centerTitle: true,
elevation: 0, elevation: 0,
//
leading: !_isSelectMode leading: !_isSelectMode
?IconButton( ? IconButton( //
onPressed: () { onPressed: () {
Get.back(); Get.back(result: true);
}, },
icon: Image.asset( icon: Image.asset(
"assets/img/back.png", "assets/img/back.png",
@ -209,13 +86,23 @@ class _MyCollectionViewState extends State<MyCollectionView> {
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
) )
: TextButton( : TextButton( //
onPressed: _selectAll, onPressed: () {
setState(() {
_selectedItems = List.generate(likedSongs.length, (index) => true);
});
},
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: Colors.black, foregroundColor: Colors.black,
minimumSize: const Size(50, 40),
padding: const EdgeInsets.symmetric(horizontal: 8),
),
child: const Text(
'全选',
style: TextStyle(fontSize: 18),
), ),
child: const Text('全选',style: TextStyle(fontSize: 18),),
), ),
//
title: _isSelectMode title: _isSelectMode
? Text( ? Text(
'已选中 ${_selectedItems.where((item) => item).length} 首歌曲', '已选中 ${_selectedItems.where((item) => item).length} 首歌曲',
@ -224,107 +111,202 @@ class _MyCollectionViewState extends State<MyCollectionView> {
), ),
) )
: const Text( : const Text(
'我的收藏', '我的点赞',
style: TextStyle(color: Colors.black), style: TextStyle(color: Colors.black),
), ),
//
actions: [ actions: [
if (_isSelectMode) if (_isSelectMode)
TextButton( TextButton(
onPressed: (){ onPressed: () {
setState(() { setState(() {
_isSelectMode = false; _isSelectMode = false;
_selectedItems = List.generate(10, (index) => false); _selectedItems = List.generate(likedSongs.length, (index) => false);
}); });
}, },
child: const Text( child: const Text(
"完成", "完成",
style: TextStyle( style: TextStyle(color: Colors.black, fontSize: 18),
color: Colors.black, ))
fontSize: 18
),
)
)
], ],
), ),
//
body: Container( body: Container(
padding: const EdgeInsets.only(left: 10),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(30)), borderRadius: BorderRadius.vertical(top: Radius.circular(30)),
), ),
child: Column( child: Column(
children: [ children: [
Row( //
mainAxisAlignment: MainAxisAlignment.spaceBetween, Padding(
children: [ padding: const EdgeInsets.symmetric(horizontal: 16.0),
Row( child: Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
IconButton( children: [
onPressed: (){}, //
icon: Image.asset( Row(
"assets/img/button_play.png", children: [
width: 20, IconButton(
height: 20, onPressed: likedSongs.isEmpty
? null
: () {
// TODO:
},
icon: Image.asset(
"assets/img/button_play.png",
width: 20,
height: 20,
),
), ),
), const Text(
const Text( '播放全部',
'播放全部', style: TextStyle(fontSize: 16),
style: TextStyle(
fontSize: 16
), ),
), const SizedBox(width: 5),
const SizedBox(width: 5,), Text(
const Text( '(${likedSongs.length})',
'50', style: const TextStyle(fontSize: 16),
style: TextStyle(
fontSize: 16
), ),
],
),
//
IconButton(
onPressed: likedSongs.isEmpty ? null : () {
setState(() {
_isSelectMode = !_isSelectMode;
if (!_isSelectMode) {
_selectedItems = List.generate(likedSongs.length, (index) => false);
}
});
},
icon: Image.asset(
"assets/img/list_op.png",
width: 20,
height: 20,
), ),
],
),
IconButton(
onPressed: _toggleSelectMode,
icon: Image.asset(
"assets/img/list_op.png",
width: 20,
height: 20,
), ),
), ],
], ),
), ),
//
Expanded( Expanded(
child: MyWorkCount == 0 child: ListView.builder(
? Center(child: Text('你还没有点赞')) // itemCount: likedSongs.length,
: ListView.builder( padding: EdgeInsets.zero,
itemCount: MyWorkCount, itemBuilder: (context, index) {
itemBuilder: (BuildContext context, int index) { final song = likedSongs[index];
return Container( return ListTile(
padding: const EdgeInsets.symmetric(vertical: 5.0), contentPadding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListTile( //
leading: _isSelectMode onTap: _isSelectMode
? Checkbox( ? () {
value: _selectedItems[index], setState(() {
onChanged: (value) { _selectedItems[index] = !_selectedItems[index];
setState(() { });
_selectedItems[index] = value!; }
}); : () async {
}, // Song
shape: const CircleBorder(), List<Song> songList = likedSongs.map((song) => Song(
activeColor: const Color(0xff429482), id: song.id ?? 0,
) title: song.name ?? '未知歌曲',
: CircleAvatar( artist: song.singerName ?? '未知歌手',
backgroundImage: NetworkImage(coverPath[index]), // artistPic: song.coverPath ?? '',
radius: 25, pic: song.coverPath ?? '',
), musicurl: song.musicPath ?? '',
title: Text('${MyWorkNames[index]} - ${singerName[index]}'), // likes: song.likes,
trailing: _isSelectMode collection: song.collection,
? null mid: song.mid,
: IconButton( )).toList();
icon: const Icon(Icons.more_vert),
onPressed: () { //
_bottomSheet(context); final result = await Navigator.push(
}, context,
), MaterialPageRoute(
builder: (context) => MusicView(
songList: songList,
initialSongIndex: index,
//
onSongStatusChanged: (index, isCollected, isLiked) {
setState(() {
songList[index].collection = isCollected;
songList[index].likes = isLiked;
downloadManager.updateSongInfo(
songList[index].id,
isCollected,
isLiked,
);
});
},
),
),
);
//
if (result != null) {
_fetchLikedSongs();
}
},
//
title: Row(
children: [
//
if (_isSelectMode)
Checkbox(
value: _selectedItems[index],
onChanged: (value) {
setState(() {
_selectedItems[index] = value!;
});
},
shape: const CircleBorder(),
activeColor: const Color(0xff429482),
),
//
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
song.coverPath ?? '',
width: 60,
height: 60,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Image.asset(
"assets/img/artist_pic.png",
width: 60,
height: 60,
);
},
),
),
const SizedBox(width: 12),
//
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
song.name ?? '未知歌曲',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 16,
color: Colors.black,
),
),
Text(
song.singerName ?? '未知歌手',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
color: Colors.black54,
),
),
],
),
),
],
), ),
); );
}, },
@ -333,51 +315,76 @@ class _MyCollectionViewState extends State<MyCollectionView> {
], ],
), ),
), ),
//
bottomNavigationBar: _isSelectMode bottomNavigationBar: _isSelectMode
? BottomAppBar( ? BottomAppBar(
child: SizedBox( height: 140,
height: 127.0, child: SingleChildScrollView(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Row( //
mainAxisAlignment: MainAxisAlignment.spaceAround, Padding(
children: [ padding: const EdgeInsets.symmetric(vertical: 8),
Row( child: Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceAround,
IconButton( children: [
onPressed: (){ // "添加到"
_showSelectionDialog(); Expanded(
child: InkWell(
onTap: () {
// TODO:
}, },
icon: Image.asset("assets/img/list_add.png"), child: Row(
iconSize: 60, mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 25,
height: 25,
child: Image.asset("assets/img/add.png"),
),
const SizedBox(width: 4),
const Text("添加到"),
],
),
), ),
const Text("添加到"), ),
], // 线
), Container(
Container( height: 50,
height: 50, width: 2,
width: 2, color: const Color(0xff429482),
color: const Color(0xff429482), ),
), // "删除"
Row( Expanded(
children: [ child: InkWell(
IconButton( onTap: () {
onPressed: (){}, // TODO:
icon: Image.asset("assets/img/list_download.png"), },
iconSize: 60, child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 22,
height: 22,
child: Image.asset("assets/img/delete.png"),
),
const SizedBox(width: 4),
const Text("删除"),
],
),
), ),
const Text("下载"), ),
], ],
), ),
],
), ),
//
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
_isSelectMode = false; _isSelectMode = false;
_selectedItems = List.generate(10, (index) => false); _selectedItems = List.generate(likedSongs.length, (index) => false);
}); });
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -388,11 +395,10 @@ class _MyCollectionViewState extends State<MyCollectionView> {
borderRadius: BorderRadius.zero, borderRadius: BorderRadius.zero,
), ),
), ),
child: const Text('取消', child: const Text(
style: TextStyle( '取消',
color: Colors.black, style: TextStyle(color: Colors.black, fontSize: 16),
fontSize: 16 ),
),),
), ),
], ],
), ),
@ -402,111 +408,4 @@ class _MyCollectionViewState extends State<MyCollectionView> {
), ),
); );
} }
Future _bottomSheet(BuildContext context){
return showModalBottomSheet(
context: context,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(30))),
builder: (context) =>Container(
height: 210,
padding: const EdgeInsets.only(top: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
IconButton(
onPressed: (){},
icon: Image.asset("assets/img/list_add.png"),
iconSize: 60,
),
const Text("加入歌单")
],
),
Column(
children: [
IconButton(
onPressed: (){},
icon: Image.asset("assets/img/list_download.png"),
iconSize: 60,
),
const Text("下载")
],
),
Column(
children: [
IconButton(
onPressed: (){},
icon: Image.asset("assets/img/list_collection.png"),
iconSize: 60,
),
const Text("收藏")
],
),
Column(
children: [
IconButton(
onPressed: (){},
icon: Image.asset("assets/img/list_good.png"),
iconSize: 60,
),
const Text("点赞")
],
),
Column(
children: [
IconButton(
onPressed: (){},
icon: Image.asset("assets/img/list_comment.png"),
iconSize: 60,
),
const Text("评论")
],
),
],
),
const SizedBox(height: 10,),
ElevatedButton(
onPressed: () {
// Get.to(()=>const MusicView());
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xffE6F4F1),
padding: const EdgeInsets.symmetric(vertical: 8),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: const Text(
"查看详情页",
style: TextStyle(color:Colors.black,fontSize: 18),
),
),
ElevatedButton(
onPressed: () =>Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xff429482),
padding: const EdgeInsets.symmetric(vertical: 8),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: const Text(
"取消",
style: TextStyle(color:Colors.black,fontSize: 18),
),
),
],
),
)
);
}
} }

@ -27,6 +27,7 @@ class _RankViewState extends State<RankView> with AutomaticKeepAliveClientMixin
List rankSingerName = []; List rankSingerName = [];
List rankCoverPath = []; List rankCoverPath = [];
List rankMusicPath = []; List rankMusicPath = [];
List rankMid = [];
List<Song> songs = []; List<Song> songs = [];
final downloadManager = Get.put(DownloadManager()); final downloadManager = Get.put(DownloadManager());
@ -57,14 +58,9 @@ class _RankViewState extends State<RankView> with AutomaticKeepAliveClientMixin
List<int> ids = bean2.data!.map((data) => data.id!).toList(); List<int> ids = bean2.data!.map((data) => data.id!).toList();
rankNames = bean2.data!.map((data) => data.name!).toList(); rankNames = bean2.data!.map((data) => data.name!).toList();
rankSingerName = bean2.data!.map((data) => data.singerName!).toList(); rankSingerName = bean2.data!.map((data) => data.singerName!).toList();
rankCoverPath = bean2.data!.map((data) => data.coverPath!).toList(); rankCoverPath = bean2.data!.map((data) => data.coverPath == '' ? 'https://api.aspark.cc/image/1/6759856d288fd.jpg' : data.coverPath).toList();
rankMusicPath = bean2.data!.map((data) => data.musicPath!).toList(); rankMusicPath = bean2.data!.map((data) => data.musicPath ?? '').toList();
rankMid = bean2.data!.map((data) => data.mid).toList();
for (int i = 0; i < ids.length; i++) {
print(ids[i]);
}
songs.clear();
if (rankNames.isNotEmpty && if (rankNames.isNotEmpty &&
rankNames.length == rankSingerName.length && rankNames.length == rankSingerName.length &&
@ -80,6 +76,7 @@ class _RankViewState extends State<RankView> with AutomaticKeepAliveClientMixin
id: ids[i], id: ids[i],
likes: null, likes: null,
collection: null, collection: null,
mid: rankMid[i],
)); ));
} }
} }
@ -272,25 +269,25 @@ class _RankViewState extends State<RankView> with AutomaticKeepAliveClientMixin
color: Colors.grey[100], color: Colors.grey[100],
child: const Icon(Icons.music_note, size: 30), child: const Icon(Icons.music_note, size: 30),
), ),
loadingBuilder: (context, child, loadingProgress) { // loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child; // if (loadingProgress == null) return child;
return Container( // return Container(
width: 60, // width: 60,
height: 60, // height: 60,
color: Colors.grey[100], // color: Colors.grey[100],
child: Center( // child: Center(
child: CircularProgressIndicator( // child: CircularProgressIndicator(
strokeWidth: 2, // strokeWidth: 2,
valueColor: const AlwaysStoppedAnimation<Color>( // valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xff429482)), // Color(0xff429482)),
value: loadingProgress.expectedTotalBytes != null // value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded / // ? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes! // loadingProgress.expectedTotalBytes!
: null, // : null,
), // ),
), // ),
); // );
}, // },
), ),
), ),
), ),
@ -354,7 +351,7 @@ class _RankViewState extends State<RankView> with AutomaticKeepAliveClientMixin
), ),
), ),
const SizedBox( const SizedBox(
height: 100, height: 130,
), ),
], ],
), ),

@ -37,11 +37,12 @@ class _SearchViewState extends State<SearchView> {
artistPic: data.coverPath!, artistPic: data.coverPath!,
title: data.name!, title: data.name!,
artist: data.singerName!, artist: data.singerName!,
musicurl: data.musicPath!, musicurl: data.musicPath,
pic: data.coverPath!, pic: data.coverPath!,
id: data.id!, id: data.id!,
likes: data.likeOrNot!, likes: data.likeOrNot!,
collection: data.collectOrNot!, collection: data.collectOrNot!,
mid: data.mid,
)); ));
} }
}); });

@ -6,17 +6,8 @@ import '../common_widget/app_data.dart';
import '../models/getRank_bean.dart'; import '../models/getRank_bean.dart';
import '../common_widget/Song_widegt.dart'; import '../common_widget/Song_widegt.dart';
import 'music_view.dart'; // MusicView import 'music_view.dart'; // MusicView
import 'package:music_player_miao/common_widget/app_data.dart';
import '../common/download_manager.dart'; import '../common/download_manager.dart';
import '../models/getRank_bean.dart';
import '../view_model/rank_view_model.dart';
import 'music_view.dart';
import '../common_widget/Song_widegt.dart';
import '../api/api_collection.dart';
import '../api/api_music_likes.dart';
import '../api/api_music_list.dart';
import '../models/universal_bean.dart';
import 'comment_view.dart';
class SongRecommendationView extends StatefulWidget { class SongRecommendationView extends StatefulWidget {
const SongRecommendationView({super.key}); const SongRecommendationView({super.key});
@ -31,6 +22,7 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
List rankSingerName = []; List rankSingerName = [];
List rankCoverPath = []; List rankCoverPath = [];
List rankMusicPath = []; List rankMusicPath = [];
List rankMid = [];
List<double> relevanceValues = []; List<double> relevanceValues = [];
List<Song> songs = []; List<Song> songs = [];
final downloadManager = Get.put(DownloadManager()); final downloadManager = Get.put(DownloadManager());
@ -71,8 +63,9 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
List<int> ids = bean2.data!.take(6).map((data) => data.id!).toList(); List<int> ids = bean2.data!.take(6).map((data) => data.id!).toList();
rankNames = bean2.data!.take(6).map((data) => data.name!).toList(); rankNames = bean2.data!.take(6).map((data) => data.name!).toList();
rankSingerName = bean2.data!.take(6).map((data) => data.singerName!).toList(); rankSingerName = bean2.data!.take(6).map((data) => data.singerName!).toList();
rankCoverPath = bean2.data!.take(6).map((data) => data.coverPath!).toList(); rankCoverPath = bean2.data!.take(6).map((data) => data.coverPath == '' ? 'https://api.aspark.cc/image/1/6759856d288fd.jpg' : data.coverPath).toList();
rankMusicPath = bean2.data!.take(6).map((data) => data.musicPath!).toList(); rankMusicPath = bean2.data!.take(6).map((data) => data.musicPath ?? '').toList();
rankMid = bean2.data!.take(6).map((data) => data.mid).toList();
songs.clear(); songs.clear();
for (int i = 0; i < rankNames.length; i++) { for (int i = 0; i < rankNames.length; i++) {
@ -85,6 +78,7 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
id: ids[i], id: ids[i],
likes: null, likes: null,
collection: null, collection: null,
mid: rankMid[i],
)); ));
relevanceValues.add(0.75); relevanceValues.add(0.75);
} }
@ -130,21 +124,25 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
return 0.6 + math.Random().nextDouble() * 0.4; return 0.6 + math.Random().nextDouble() * 0.4;
} }
double _getCircleSize(double relevance) { double _getCircleSize(double relevance, Size screenSize) {
return 20.0 + (relevance * 130); return 0.08 * screenSize.width + (relevance * 80); //
} }
Map<String, double> _getCirclePosition(int index, int totalItems, Size screenSize) { //
final radius = math.min(screenSize.width, screenSize.height) * 0.28; Map<String, double> _getCirclePosition(int index, int totalItems, Size screenSize, Offset refreshButtonPosition) {
final angle = (index * 2 * math.pi / totalItems) - math.pi / 2; final radius = math.min(screenSize.width, screenSize.height) * 0.28; //
final angle = (index * 2 * math.pi / totalItems) - math.pi / 2; //
return { return {
'left': radius * math.cos(angle), 'left': refreshButtonPosition.dx + radius * math.cos(angle), //
'top': radius * math.sin(angle), 'top': refreshButtonPosition.dy + radius * math.sin(angle), //
}; };
} }
//
Color _getCircleColor(double relevance) { Color _getCircleColor(double relevance) {
return Color(0xFFB2FF59).withOpacity(0.3); return Color(0xFFB2FF59).withOpacity(0.3); // 使绿
} }
@override @override
@ -152,6 +150,9 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
final screenSize = MediaQuery.of(context).size; final screenSize = MediaQuery.of(context).size;
final safeAreaPadding = MediaQuery.of(context).padding; final safeAreaPadding = MediaQuery.of(context).padding;
//
final refreshButtonPosition = Offset(screenSize.width / 2, screenSize.height / 2);
return Container( return Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
image: DecorationImage( image: DecorationImage(
@ -183,7 +184,7 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
// 线 // 线
CustomPaint( CustomPaint(
size: Size(screenSize.width, screenSize.height), size: Size(screenSize.width, screenSize.height),
painter: LinePainter( painter: LinePainter(
@ -191,6 +192,7 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
relevanceValues: relevanceValues, relevanceValues: relevanceValues,
screenSize: screenSize, screenSize: screenSize,
lineAnimation: _animationController, lineAnimation: _animationController,
refreshButtonPosition: refreshButtonPosition,
), ),
), ),
// //
@ -199,13 +201,13 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
children: List.generate(6, (index) { children: List.generate(6, (index) {
final song = songs[index]; final song = songs[index];
final relevance = relevanceValues[index]; final relevance = relevanceValues[index];
final size = _getCircleSize(relevance); final size = _getCircleSize(relevance, screenSize);
final position = _getCirclePosition(index, songs.length, screenSize); final position = _getCirclePosition(index, songs.length, screenSize, refreshButtonPosition);
return AnimatedPositioned( return AnimatedPositioned(
duration: Duration(milliseconds: 500), duration: Duration(milliseconds: 500),
left: screenSize.width / 2 + position['left']! - size / 2, left: position['left']! - size / 2,
top: screenSize.height / 2 + position['top']! - size / 2 - safeAreaPadding.top + 1, // top: position['top']! - size / 2 - safeAreaPadding.top + 1,
child: FadeTransition( child: FadeTransition(
opacity: _circleFadeInAnimations.isNotEmpty && index < _circleFadeInAnimations.length opacity: _circleFadeInAnimations.isNotEmpty && index < _circleFadeInAnimations.length
? _circleFadeInAnimations[index] ? _circleFadeInAnimations[index]
@ -250,13 +252,13 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
animation: dotController, animation: dotController,
builder: (context, child) { builder: (context, child) {
final progress = dotController.value; final progress = dotController.value;
final position = _getCirclePosition(index, songs.length, screenSize); final position = _getCirclePosition(index, songs.length, screenSize, refreshButtonPosition);
final radius = math.min(screenSize.width, screenSize.height) * 0.28; final radius = math.min(screenSize.width, screenSize.height) * 0.28;
final angle = (index * 2 * math.pi / songs.length) - math.pi / 2; final angle = (index * 2 * math.pi / songs.length) - math.pi / 2;
final dotPosition = Offset( final dotPosition = Offset(
screenSize.width / 2 + position['left']! - progress * (radius - 60) * math.cos(angle), position['left']! - progress * (radius - 60) * math.cos(angle),
screenSize.height / 2 + position['top']! - progress * (radius - 60) * math.sin(angle), position['top']! - progress * (radius - 60) * math.sin(angle),
); );
return Positioned( return Positioned(
@ -323,7 +325,54 @@ class _SongRecommendationViewState extends State<SongRecommendationView> with Ti
} }
} }
// Circle button widget class LinePainter extends CustomPainter {
final List recommendedSongs;
final List<double> relevanceValues;
final Size screenSize;
final Animation<double> lineAnimation;
final Offset refreshButtonPosition;
LinePainter({
required this.recommendedSongs,
required this.relevanceValues,
required this.screenSize,
required this.lineAnimation,
required this.refreshButtonPosition,
});
@override
void paint(Canvas canvas, Size size) {
final center = refreshButtonPosition; // 使
for (int i = 0; i < recommendedSongs.length; i++) {
final relevance = relevanceValues[i];
final position = _getCirclePosition(i, recommendedSongs.length, size);
final end = Offset(center.dx + position['left']!, center.dy + position['top']!);
final paint = Paint()
..color = Colors.grey.withOpacity(0.2) // 0.5
..strokeWidth = 0.01 + math.pow(relevance, 2) * 10;
// 线
canvas.drawLine(center, end, paint);
}
}
Map<String, double> _getCirclePosition(int index, int totalItems, Size size) {
final radius = math.min(size.width, size.height) * 0.28;
final angle = (index * 2 * math.pi / totalItems) - math.pi / 2;
return {
'left': radius * math.cos(angle),
'top': radius * math.sin(angle),
};
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
class SongCircleButton extends StatelessWidget { class SongCircleButton extends StatelessWidget {
final double relevance; final double relevance;
final double size; final double size;
@ -346,7 +395,7 @@ class SongCircleButton extends StatelessWidget {
}); });
Color _getCircleColor() { Color _getCircleColor() {
return Color(0xFFFFC1E3); return Color(0xFFFFC1E3); //
} }
@override @override
@ -357,26 +406,22 @@ class SongCircleButton extends StatelessWidget {
width: size, width: size,
height: size, height: size,
decoration: BoxDecoration( decoration: BoxDecoration(
color: _getCircleColor().withOpacity(0.9), color: _getCircleColor(),
borderRadius: BorderRadius.circular(size / 2), //
borderRadius: BorderRadius.circular(size / 2),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: _getCircleColor().withOpacity(0.6), // color: _getCircleColor().withOpacity(0.6), //
blurRadius: 12, // blurRadius: 12, //
spreadRadius: 8, // spreadRadius: 8, //
), ),
],// ],
), ),
child: Center( child: Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
//
Text( Text(
' ', '',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 18, fontSize: 18,
@ -384,7 +429,6 @@ class SongCircleButton extends StatelessWidget {
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
//
Text( Text(
songTitle, songTitle,
textAlign: TextAlign.center, textAlign: TextAlign.center,
@ -395,7 +439,6 @@ class SongCircleButton extends StatelessWidget {
), ),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
//
Text( Text(
artistName, artistName,
textAlign: TextAlign.center, textAlign: TextAlign.center,
@ -411,48 +454,3 @@ class SongCircleButton extends StatelessWidget {
); );
} }
} }
class LinePainter extends CustomPainter {
final List recommendedSongs;
final List<double> relevanceValues;
final Size screenSize;
final Animation<double> lineAnimation;
LinePainter({
required this.recommendedSongs,
required this.relevanceValues,
required this.screenSize,
required this.lineAnimation,
});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
for (int i = 0; i < recommendedSongs.length; i++) {
final relevance = relevanceValues[i];
final position = _getCirclePosition(i, recommendedSongs.length, size);
final end = Offset(center.dx + position['left']!, center.dy + position['top']!);
final paint = Paint()
..color = Colors.grey.withOpacity(0.2) // 0.5
..strokeWidth = 0.01 + math.pow(relevance, 2) * 10;
canvas.drawLine(center, end, paint);
}
}
Map<String, double> _getCirclePosition(int index, int totalItems, Size size) {
final radius = math.min(size.width, size.height) * 0.28;
final angle = (index * 2 * math.pi / totalItems) - math.pi / 2;
return {
'left': radius * math.cos(angle),
'top': radius * math.sin(angle),
};
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}

@ -215,6 +215,7 @@ class _MyLikesViewState extends State<MyLikesView> {
musicurl: song.musicPath ?? '', musicurl: song.musicPath ?? '',
likes: song.likes, likes: song.likes,
collection: song.collection, collection: song.collection,
mid: song.mid,
)).toList(); )).toList();
// //

Loading…
Cancel
Save