|
|
|
@ -1,10 +1,13 @@
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
import '../api/api_music_likes.dart';
|
|
|
|
|
import '../api/api_music_list.dart';
|
|
|
|
|
import '../api/api_music_return.dart';
|
|
|
|
|
import '../common/download_manager.dart';
|
|
|
|
|
import '../common_widget/Song_widegt.dart';
|
|
|
|
|
import '../common_widget/app_data.dart';
|
|
|
|
|
import '../models/MusicsListBean.dart';
|
|
|
|
|
import '../models/universal_bean.dart';
|
|
|
|
|
import 'main_tab_view/main_tab_view.dart';
|
|
|
|
|
import 'music_view.dart';
|
|
|
|
|
|
|
|
|
@ -18,12 +21,23 @@ class SearchView extends StatefulWidget {
|
|
|
|
|
class _SearchViewState extends State<SearchView> {
|
|
|
|
|
final List<Song> songs = [];
|
|
|
|
|
final TextEditingController _searchController = TextEditingController();
|
|
|
|
|
final FocusNode _searchFocusNode = FocusNode(); // 添加焦点控制
|
|
|
|
|
final downloadManager = Get.put(DownloadManager());
|
|
|
|
|
bool isSearching = false;
|
|
|
|
|
String lastSearchQuery = '';
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
_loadRecommendData();
|
|
|
|
|
_searchController.addListener(_onSearchChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
_searchController.dispose();
|
|
|
|
|
_searchFocusNode.dispose();
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _loadRecommendData() async {
|
|
|
|
@ -49,6 +63,72 @@ class _SearchViewState extends State<SearchView> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索内容变化的处理
|
|
|
|
|
void _onSearchChanged() {
|
|
|
|
|
final query = _searchController.text.trim();
|
|
|
|
|
if (query.isEmpty) {
|
|
|
|
|
// 如果搜索框为空,显示推荐列表
|
|
|
|
|
if (lastSearchQuery.isNotEmpty) {
|
|
|
|
|
setState(() {
|
|
|
|
|
isSearching = false;
|
|
|
|
|
lastSearchQuery = '';
|
|
|
|
|
});
|
|
|
|
|
_loadRecommendData();
|
|
|
|
|
}
|
|
|
|
|
} else if (query != lastSearchQuery) {
|
|
|
|
|
// 当搜索内容发生变化且不为空时
|
|
|
|
|
setState(() {
|
|
|
|
|
isSearching = true;
|
|
|
|
|
lastSearchQuery = query;
|
|
|
|
|
});
|
|
|
|
|
_debounceSearch(query);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 防抖搜索
|
|
|
|
|
Future<void> _debounceSearch(String query) async {
|
|
|
|
|
// 等待300ms后执行搜索,避免频繁请求
|
|
|
|
|
await Future.delayed(const Duration(milliseconds: 300));
|
|
|
|
|
if (query == _searchController.text.trim()) {
|
|
|
|
|
_searchSongs(query);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索歌曲的API调用
|
|
|
|
|
Future<void> _searchSongs(String keyword) async {
|
|
|
|
|
try {
|
|
|
|
|
final response = await SearchMusic().search(
|
|
|
|
|
keyword: keyword,
|
|
|
|
|
Authorization: AppData().currentToken,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (response.code == 200 && response.data != null) {
|
|
|
|
|
setState(() {
|
|
|
|
|
songs.clear();
|
|
|
|
|
for (var data in response.data!) {
|
|
|
|
|
songs.add(Song(
|
|
|
|
|
artistPic: data.coverPath ?? 'https://api.aspark.cc/image/1/6759856d288fd.jpg',
|
|
|
|
|
title: data.name!,
|
|
|
|
|
artist: data.singerName!,
|
|
|
|
|
musicurl: data.musicPath,
|
|
|
|
|
pic: data.coverPath ?? 'https://api.aspark.cc/image/1/6759856d288fd.jpg',
|
|
|
|
|
id: data.id!,
|
|
|
|
|
likes: data.likeOrNot,
|
|
|
|
|
collection: data.collectOrNot,
|
|
|
|
|
mid: data.mid,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
print('Search error: $error');
|
|
|
|
|
// 可以添加错误提示
|
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
|
const SnackBar(content: Text('搜索失败,请稍后重试')),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Container(
|
|
|
|
@ -60,18 +140,35 @@ class _SearchViewState extends State<SearchView> {
|
|
|
|
|
),
|
|
|
|
|
child: Scaffold(
|
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
|
body: Column(
|
|
|
|
|
children: [
|
|
|
|
|
const SizedBox(height: 60),
|
|
|
|
|
_buildSearchBar(),
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
_buildPlayAllButton(),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: _buildSongsList(),
|
|
|
|
|
),
|
|
|
|
|
MiniPlayer(),
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
],
|
|
|
|
|
body: GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
if (_searchFocusNode.hasFocus) {
|
|
|
|
|
_searchFocusNode.unfocus();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
behavior: HitTestBehavior.translucent, // 确保手势能被检测到
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
const SizedBox(height: 60),
|
|
|
|
|
_buildSearchBar(),
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
_buildPlayAllButton(),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: NotificationListener<ScrollNotification>(
|
|
|
|
|
onNotification: (scrollNotification) {
|
|
|
|
|
if (scrollNotification is ScrollStartNotification) {
|
|
|
|
|
if (_searchFocusNode.hasFocus) {
|
|
|
|
|
_searchFocusNode.unfocus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
child: _buildSongsList(),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
MiniPlayer(),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
@ -95,14 +192,29 @@ class _SearchViewState extends State<SearchView> {
|
|
|
|
|
),
|
|
|
|
|
child: TextField(
|
|
|
|
|
controller: _searchController,
|
|
|
|
|
focusNode: _searchFocusNode,
|
|
|
|
|
decoration: InputDecoration(
|
|
|
|
|
border: InputBorder.none,
|
|
|
|
|
hintText: '搜索你想找的音乐',
|
|
|
|
|
hintStyle: TextStyle(color: Colors.grey.shade400),
|
|
|
|
|
prefixIcon: Icon(Icons.search, color: Colors.grey.shade400),
|
|
|
|
|
suffixIcon: _searchController.text.isNotEmpty
|
|
|
|
|
? IconButton(
|
|
|
|
|
icon: Icon(Icons.clear, color: Colors.grey.shade400),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
_searchController.clear();
|
|
|
|
|
_searchFocusNode.unfocus();
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
: null,
|
|
|
|
|
contentPadding:
|
|
|
|
|
const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
|
|
|
|
|
),
|
|
|
|
|
onSubmitted: (value) {
|
|
|
|
|
if (value.isNotEmpty) {
|
|
|
|
|
_searchSongs(value);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
@ -233,14 +345,43 @@ class _SearchViewState extends State<SearchView> {
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
icon: Icon(
|
|
|
|
|
Icons.more_vert,
|
|
|
|
|
color: Colors.grey.shade400,
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.only(right: 16),
|
|
|
|
|
child: InkWell(
|
|
|
|
|
onTap: () async {
|
|
|
|
|
setState(() {
|
|
|
|
|
songs[index].likes = !songs[index].likes!;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
UniversalBean response = await LikesApiMusic()
|
|
|
|
|
.likesMusic(
|
|
|
|
|
musicId: song.id,
|
|
|
|
|
Authorization: AppData().currentToken);
|
|
|
|
|
|
|
|
|
|
if (response.code != 200) {
|
|
|
|
|
setState(() {
|
|
|
|
|
songs[index].likes = !songs[index].likes!;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
child: song.likes!
|
|
|
|
|
? Image.asset(
|
|
|
|
|
'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,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
// 实现更多操作的弹出菜单
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|