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'; class SearchView extends StatefulWidget { const SearchView({super.key}); @override State createState() => _SearchViewState(); } class _SearchViewState extends State { final List 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); Future.delayed(const Duration(milliseconds: 500), () { if (mounted) { _searchFocusNode.requestFocus(); } }); } @override void dispose() { _searchController.dispose(); _searchFocusNode.dispose(); super.dispose(); } void _loadRecommendData() async { MusicsListBean bean = await GetMusic() .getMusicList(Authorization: AppData().currentToken, num: 10); if (bean.code == 200) { setState(() { songs.clear(); for (var data in bean.data!) { songs.add(Song( artistPic: data.coverPath!, title: data.name!, artist: data.singerName!, musicurl: data.musicPath, pic: data.coverPath!, id: data.id!, likes: data.likeOrNot!, collection: data.collectOrNot!, mid: data.mid, )); } }); } } // 搜索内容变化的处理 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 _debounceSearch(String query) async { // 等待300ms后执行搜索,避免频繁请求 await Future.delayed(const Duration(milliseconds: 300)); if (query == _searchController.text.trim()) { _searchSongs(query); } } // 搜索歌曲的API调用 Future _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?1', title: data.name!, artist: data.singerName!, musicurl: data.musicPath, pic: data.coverPath ?? 'https://api.aspark.cc/image/1/6759856d288fd.jpg?1', 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( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/img/app_bg.png"), fit: BoxFit.cover, ), ), child: Scaffold( backgroundColor: Colors.transparent, 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( onNotification: (scrollNotification) { if (scrollNotification is ScrollStartNotification) { if (_searchFocusNode.hasFocus) { _searchFocusNode.unfocus(); } } return true; }, 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, 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); } }, ), ), ); } 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, ), ], ), ), 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, ), ), ), ), ], ), ), ), ); } 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); }); } }