feat: 添加搜索页面,优化封面显示

chen
Spark 1 year ago
parent 2668279899
commit ffc5c95a55

@ -1 +1 @@
d1b13d2690838e4bbcb8a4bdf95ce8b4
7d34a1186938d51f8f2b6577f7a6094e

File diff suppressed because one or more lines are too long

@ -1,4 +1,4 @@
#Sat Dec 07 23:40:58 CST 2024
#Tue Dec 10 17:44:42 CST 2024
com.example.music_player_miao.app-main-42\:/anim/fragment_fast_out_extra_slow_in.xml=E\:\\Desktop\\MTMusic\\android\\app\\build\\intermediates\\merged_res\\debug\\anim_fragment_fast_out_extra_slow_in.xml.flat
com.example.music_player_miao.app-main-42\:/drawable-v21/launch_background.xml=E\:\\Desktop\\MTMusic\\android\\app\\build\\intermediates\\merged_res\\debug\\drawable-v21_launch_background.xml.flat
com.example.music_player_miao.app-main-42\:/mipmap-hdpi/ic_launcher.png=E\:\\Desktop\\MTMusic\\android\\app\\build\\intermediates\\merged_res\\debug\\mipmap-hdpi_ic_launcher.png.flat

@ -4,7 +4,7 @@
"outputFile": "com.example.music_player_miao.app-mergeDebugResources-38:/values-night-v8/values-night-v8.xml",
"map": [
{
"source": "D:\\MTMusic\\android\\app\\src\\main\\res\\values-night\\styles.xml",
"source": "E:\\Desktop\\MTMusic\\android\\app\\src\\main\\res\\values-night\\styles.xml",
"from": {
"startLines": "3,14",
"startColumns": "4,4",

@ -95,15 +95,6 @@ class AudioPlayerController extends GetxController {
_initializePlayer();
}
// void clearState() {
// // 使 runInAction
// Get.engine.addPostFrameCallback((_) {
// position.value = Duration.zero;
// duration.value = Duration.zero;
// isLoading.value = false;
// });
// }
void syncPlayingState() {
if (_audioPlayer != null) {
isPlaying.value = _audioPlayer!.playing;
@ -207,6 +198,11 @@ class AudioPlayerController extends GetxController {
try {
final localSong = downloadManager.getLocalSong(songList[currentSongIndex.value].id);
if (localSong == null && songUrls[currentSongIndex.value] == '') {
// TODO
}
final audioSource = localSong != null
? AudioSource.file(localSong.musicurl!)
: AudioSource.uri(Uri.parse(songUrls[currentSongIndex.value]));

@ -5,6 +5,7 @@ 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/models/search_bean.dart';
import 'package:music_player_miao/view/comment_view.dart';
import 'package:music_player_miao/view/search_view.dart';
import '../../view_model/home_view_model.dart';
import '../api/api_music_likes.dart';
import '../api/api_music_list.dart';
@ -29,7 +30,8 @@ class HomeView extends StatefulWidget {
class _HomeViewState extends State<HomeView>
with AutomaticKeepAliveClientMixin {
final homeVM = Get.put(HomeViewModel());
final TextEditingController _controller = TextEditingController();
// final TextEditingController _controller = TextEditingController();
bool _isSearching = false;
final downloadManager = Get.put(DownloadManager());
List<Song> selectedSongs = [];
@ -55,8 +57,8 @@ class _HomeViewState extends State<HomeView>
Future<void> _fetchSonglistData() async {
try {
MusicsListBean bean =
await GetMusic().getMusicList(Authorization: AppData().currentToken, num: 10);
MusicsListBean bean = await GetMusic()
.getMusicList(Authorization: AppData().currentToken, num: 10);
setState(() {
selectedSongs = [];
for (var data in bean.data!) {
@ -201,10 +203,13 @@ class _HomeViewState extends State<HomeView>
activeColor: const Color(0xff429482),
),
),
autoplay: true, //
loop: true, //
autoplay: true,
//
loop: true,
//
autoplayDelay: 5000,
control: const SwiperControl(color: Colors.transparent), //
control: const SwiperControl(color: Colors.transparent),
//
viewportFraction: 1.0, // 1.0
);
@ -245,86 +250,53 @@ class _HomeViewState extends State<HomeView>
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
children: [
Container(
height: 38,
decoration: BoxDecoration(
color: const Color(0xffF9F2AF),
borderRadius: BorderRadius.circular(19),
boxShadow: const [
BoxShadow(
color: Colors.black26,
offset: Offset(0, 1),
blurRadius: 0.1,
)
],
),
child: TextField(
controller: _controller,
onChanged: (query) {
setState(() async {
_filterData(query);
});
},
decoration: InputDecoration(
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 20,
),
prefixIcon: Container(
margin: const EdgeInsets.only(left: 20),
alignment: Alignment.centerLeft,
width: 30,
child: Image.asset(
"assets/img/home_search.png",
width: 20,
height: 20,
fit: BoxFit.contain,
),
),
hintText: "大家都在搜《背对背拥抱》",
hintStyle: const TextStyle(
color: Color(0xffA5A5A5),
fontSize: 13,
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SearchView(),
),
),
),
),
if (_isSearching)
Container(
height: 150,
width: 345,
);
},
child: Container(
height: 38,
decoration: BoxDecoration(
color: const Color(0xffF9F2AF).withOpacity(0.7),
color: const Color(0xffF9F2AF),
borderRadius: BorderRadius.circular(19),
boxShadow: const [
BoxShadow(
color: Colors.black26,
offset: Offset(0, 1),
blurRadius: 0.1,
)
],
),
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: _filteredData.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_filteredData[index].title),
onTap: () {
//
Navigator.push(
// 使 Navigator
context,
MaterialPageRoute(
// MusicView
builder: (context) => MusicView(
songList: _filteredData,
//
initialSongIndex:
index, //
),
),
);
},
);
},
child: Row(
children: [
Container(
margin: const EdgeInsets.only(left: 20),
alignment: Alignment.centerLeft,
width: 30,
child: Image.asset(
"assets/img/home_search.png",
width: 20,
height: 20,
fit: BoxFit.contain,
),
),
const SizedBox(width: 10),
const Text(
"大家都在搜《背对背拥抱》",
style: TextStyle(
color: Color(0xffA5A5A5),
fontSize: 13,
),
),
],
),
),
),
],
),
),
@ -386,20 +358,58 @@ class _HomeViewState extends State<HomeView>
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
leading: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
selectedSongs[index].pic,
width: 60,
height: 60,
fit: BoxFit.cover,
leading: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
selectedSongs[index].pic,
width: 60,
height: 60,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
Container(
width: 60,
height: 60,
color: Colors.grey[100],
child: const Icon(Icons.music_note, size: 30),
),
// loadingBuilder: (context, child, loadingProgress) {
// if (loadingProgress == null) return child;
// return Container(
// width: 60,
// height: 60,
// color: Colors.grey[100],
// child: Center(
// child: CircularProgressIndicator(
// strokeWidth: 2,
// valueColor: const AlwaysStoppedAnimation<Color>(
// Color(0xff429482)),
// value: loadingProgress.expectedTotalBytes != null
// ? loadingProgress.cumulativeBytesLoaded /
// loadingProgress.expectedTotalBytes!
// : null,
// ),
// ),
// );
// },
),
),
),
title: Text(
selectedSongs[index].title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
fontSize: 18,
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
@ -407,8 +417,8 @@ class _HomeViewState extends State<HomeView>
subtitle: Text(
selectedSongs[index].artist,
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
fontSize: 16,
color: Colors.grey[600],
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
@ -418,40 +428,37 @@ class _HomeViewState extends State<HomeView>
child: InkWell(
onTap: () async {
setState(() {
selectedSongs[index].likes =
!selectedSongs[index].likes!;
selectedSongs[index].likes = !selectedSongs[index].likes!;
});
UniversalBean response = await LikesApiMusic()
.likesMusic(
musicId: selectedSongs[index].id,
Authorization:
AppData().currentToken);
musicId: selectedSongs[index].id,
Authorization: AppData().currentToken);
if (response.code != 200) {
setState(() {
selectedSongs[index].likes =
!selectedSongs[index].likes!;
selectedSongs[index].likes = !selectedSongs[index].likes!;
});
}
},
child: selectedSongs[index].likes!
? Image.asset(
'assets/img/like.png',
width: 24,
height: 24,
)
'assets/img/like.png',
width: 24,
height: 24,
)
: ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.grey[700]!,
BlendMode.srcIn,
),
child: Image.asset(
'assets/img/unlike.png',
width: 24,
height: 24,
),
),
colorFilter: ColorFilter.mode(
Colors.grey[700]!,
BlendMode.srcIn,
),
child: Image.asset(
'assets/img/unlike.png',
width: 24,
height: 24,
),
),
),
),
onTap: () {
@ -466,10 +473,9 @@ class _HomeViewState extends State<HomeView>
selectedSongs[index].collection = isCollected;
selectedSongs[index].likes = isLiked;
downloadManager.updateSongInfo(
selectedSongs[index].id,
isCollected,
isLiked
);
selectedSongs[index].id,
isCollected,
isLiked);
});
},
),
@ -528,7 +534,7 @@ class _HomeViewState extends State<HomeView>
const Text("加入歌单"),
],
),
//
Column(
children: [
@ -553,7 +559,8 @@ class _HomeViewState extends State<HomeView>
});
// 2. API
UniversalBean response = await CollectionApiMusic().addCollection(
UniversalBean response =
await CollectionApiMusic().addCollection(
musicId: selectedSongs[index].id,
Authorization: AppData().currentToken,
);
@ -563,26 +570,28 @@ class _HomeViewState extends State<HomeView>
// 3.1 API
setState(() {
collectionsnot = !collectionsnot;
selectedSongs[index].collection = collectionsnot;
selectedSongs[index].collection =
collectionsnot;
});
} else {
// 3.2 API
downloadManager.updateSongInfo(
selectedSongs[index].id, // ID
collectionsnot, //
selectedSongs[index].likes ?? false //
);
selectedSongs[index].id, // ID
collectionsnot, //
selectedSongs[index].likes ??
false //
);
}
},
icon: SizedBox(
width: 60,
height: 60,
child: Image.asset(
//
collectionsnot
? "assets/img/list_collection.png" //
: "assets/img/list_collection_un.png" //
),
//
collectionsnot
? "assets/img/list_collection.png" //
: "assets/img/list_collection_un.png" //
),
),
),
const Text("收藏"),
@ -601,7 +610,8 @@ class _HomeViewState extends State<HomeView>
});
// 2. API
UniversalBean response = await LikesApiMusic().likesMusic(
UniversalBean response =
await LikesApiMusic().likesMusic(
musicId: selectedSongs[index].id,
Authorization: AppData().currentToken,
);
@ -616,21 +626,22 @@ class _HomeViewState extends State<HomeView>
} else {
// 3.2 API
downloadManager.updateSongInfo(
selectedSongs[index].id, // ID
selectedSongs[index].collection ?? false, //
likesnot //
);
selectedSongs[index].id, // ID
selectedSongs[index].collection ?? false,
//
likesnot //
);
}
},
icon: SizedBox(
width: 60,
height: 60,
child: Image.asset(
//
likesnot
? "assets/img/list_good.png" //
: "assets/img/list_good_un.png" //
),
//
likesnot
? "assets/img/list_good.png" //
: "assets/img/list_good_un.png" //
),
),
),
const Text("点赞"),

@ -191,9 +191,19 @@ class MiniPlayer extends StatelessWidget {
//
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: Hero(
tag: 'album_cover',
flightShuttleBuilder: (
@ -203,7 +213,6 @@ class MiniPlayer extends StatelessWidget {
BuildContext fromHeroContext,
BuildContext toHeroContext,
) {
// Hero widget
return ClipRRect(
borderRadius: BorderRadius.circular(8),
clipBehavior: Clip.hardEdge,
@ -213,6 +222,8 @@ class MiniPlayer extends StatelessWidget {
child: Image.network(
audioController.songList[audioController.currentSongIndex.value].pic,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
const Icon(Icons.music_note, size: 30),
),
),
);
@ -223,8 +234,7 @@ class MiniPlayer extends StatelessWidget {
child: Obx(() {
final currentSong = audioController.songList.isEmpty
? null
: audioController.songList[
audioController.currentSongIndex.value];
: audioController.songList[audioController.currentSongIndex.value];
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: currentSong != null
@ -234,40 +244,29 @@ class MiniPlayer extends StatelessWidget {
width: 48,
height: 48,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Image.asset(
'assets/img/artist_pic.png',
fit: BoxFit.cover,
width: 48,
height: 48,
);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.grey[100],
width: 48,
height: 48,
child: Center(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xff429482)),
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
errorBuilder: (context, error, stackTrace) =>
const Icon(Icons.music_note, size: 30),
// loadingBuilder: (context, child, loadingProgress) {
// if (loadingProgress == null) return child;
// return Container(
// color: Colors.grey[100],
// width: 48,
// height: 48,
// child: Center(
// child: CircularProgressIndicator(
// strokeWidth: 2,
// valueColor: const AlwaysStoppedAnimation<Color>(
// Color(0xff429482)),
// value: loadingProgress.expectedTotalBytes != null
// ? loadingProgress.cumulativeBytesLoaded /
// loadingProgress.expectedTotalBytes!
// : null,
// ),
// ),
// );
// },
)
: Image.asset(
'assets/img/artist_pic.png',
width: 48,
height: 48,
fit: BoxFit.cover,
),
: const Icon(Icons.music_note, size: 30),
);
}),
),

@ -247,13 +247,51 @@ class _RankViewState extends State<RankView> with AutomaticKeepAliveClientMixin
),
),
const SizedBox(width: 10),
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
rankCoverPath[index],
width: 60,
height: 60,
fit: BoxFit.cover,
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
rankCoverPath[index],
width: 60,
height: 60,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
Container(
width: 60,
height: 60,
color: Colors.grey[100],
child: const Icon(Icons.music_note, size: 30),
),
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
width: 60,
height: 60,
color: Colors.grey[100],
child: Center(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xff429482)),
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
),
),
),
const SizedBox(width: 20),

@ -0,0 +1,275 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../common/download_manager.dart';
import '../common_widget/Song_widegt.dart';
import 'main_tab_view/main_tab_view.dart';
import 'music_view.dart';
class SearchView extends StatefulWidget {
const SearchView({super.key});
@override
State<SearchView> createState() => _SearchViewState();
}
class _SearchViewState extends State<SearchView> {
final List<Song> songs = [];
final TextEditingController _searchController = TextEditingController();
final downloadManager = Get.put(DownloadManager());
@override
void initState() {
super.initState();
_loadMockData();
}
void _loadMockData() {
//
final mockSongs = [
Song(
id: 1,
title: "夜曲",
artist: "周杰伦",
artistPic: "https://example.com/jay.jpg",
pic: "https://example.com/ye.jpg",
musicurl: "https://example.com/music1.mp3",
likes: false,
collection: false,
),
Song(
id: 2,
title: "泡沫",
artist: "邓紫棋",
artistPic: "https://example.com/gem.jpg",
pic: "https://example.com/foam.jpg",
musicurl: "https://example.com/music2.mp3",
likes: false,
collection: false,
),
//
];
setState(() {
songs.clear();
songs.addAll(mockSongs);
});
}
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/img/app_bg.png"),
fit: BoxFit.cover,
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Column(
children: [
const SizedBox(height: 60),
_buildSearchBar(),
const SizedBox(height: 20),
_buildPlayAllButton(),
Expanded(
child: _buildSongsList(),
),
MiniPlayer(),
],
),
),
);
}
Widget _buildSearchBar() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: '搜索音乐、歌手',
hintStyle: TextStyle(color: Colors.grey.shade400),
prefixIcon: Icon(Icons.search, color: Colors.grey.shade400),
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
),
),
),
);
}
Widget _buildPlayAllButton() {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: ListTile(
onTap: () {
if (songs.isNotEmpty) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MusicView(
songList: songs,
initialSongIndex: 0,
onSongStatusChanged: _updateSongStatus,
),
),
);
}
},
leading: const Icon(Icons.play_circle_fill, color: Colors.blueGrey, size: 30),
title: const Text(
'播放全部',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
trailing: Text(
'${songs.length}',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
),
),
),
);
}
Widget _buildSongsList() {
return Container(
margin: const EdgeInsets.only(top: 10),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(30)),
),
child: ListView.builder(
padding: const EdgeInsets.only(top: 10),
itemCount: songs.length,
itemBuilder: (context, index) => _buildSongItem(index),
),
);
}
Widget _buildSongItem(int index) {
final song = songs[index];
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 5),
child: InkWell(
onTap: () => _onSongTap(index),
borderRadius: BorderRadius.circular(15),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
song.pic,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
const Icon(Icons.music_note, size: 30),
),
),
),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
song.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
song.artist,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
IconButton(
icon: Icon(
Icons.more_vert,
color: Colors.grey.shade400,
),
onPressed: () {
//
},
),
],
),
),
),
);
}
void _onSongTap(int index) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MusicView(
songList: songs,
initialSongIndex: index,
onSongStatusChanged: _updateSongStatus,
),
),
);
}
void _updateSongStatus(int index, bool isCollected, bool isLiked) {
setState(() {
songs[index].collection = isCollected;
songs[index].likes = isLiked;
downloadManager.updateSongInfo(songs[index].id, isCollected, isLiked);
});
}
}
Loading…
Cancel
Save