diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 70f4cbb..386387b 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -7,6 +7,9 @@ import 'webdav_setup_page.dart'; import 'settings_page.dart'; import 'playlists_page.dart'; import '../database/song_database.dart'; +import '../models/playlist_summary.dart'; +import '../repositories/playlist_repository.dart'; +import 'playlist_detail_page.dart'; import '../widgets/song_artwork.dart'; import 'webdav_file_list_page.dart'; import '../constants/ui_constants.dart'; @@ -158,6 +161,10 @@ class _HomePageState extends BaseState { List _favorites = []; bool _isLoading = true; + /// 0 = 我的收藏,1 = 歌单列表(左右滑动切换) + int _homeTab = 0; + List _playlistSummaries = []; + // ========== 滚动控制 ========== late final ScrollController _scrollController; final GlobalKey _mediaLibraryKey = GlobalKey(); @@ -241,6 +248,7 @@ class _HomePageState extends BaseState { try { await WebDAVService.instance.loadCredentials(); await _loadFavoritesFromDb(); + await _loadPlaylistSummaries(); } catch (e) { _favorites = []; } finally { @@ -343,6 +351,223 @@ class _HomePageState extends BaseState { } } + Future _loadPlaylistSummaries() async { + try { + final list = await PlaylistRepository().getSummaries(); + if (!mounted) return; + setState(() => _playlistSummaries = list); + } catch (e) { + debugPrint('⚠️ [Home] 读取歌单失败: $e'); + } + } + + Widget _buildHomeTabIndicator() { + Widget seg(int index) { + final active = _homeTab == index; + return GestureDetector( + onTap: () => safeSetState(() => _homeTab = index), + behavior: HitTestBehavior.opaque, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 2), + child: AnimatedContainer( + duration: const Duration(milliseconds: 220), + curve: Curves.easeOutCubic, + width: active ? 26 : 7, + height: 7, + decoration: BoxDecoration( + color: active ? Colors.white : Colors.white24, + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ); + } + + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + seg(0), + const SizedBox(width: 6), + seg(1), + ], + ); + } + + Widget _buildHomeSliver() { + if (_isLoading) { + return const SliverFillRemaining( + child: Center( + child: CircularProgressIndicator(color: Color(0xFFB8D4D0)), + ), + ); + } + return _homeTab == 0 ? _buildFavoritesSliver() : _buildPlaylistsSliver(); + } + + Widget _buildFavoritesSliver() { + if (_favorites.isEmpty) { + return SliverFillRemaining( + child: AnimatedOpacity( + opacity: 1.0 - _listReveal * 0.3, + duration: const Duration(milliseconds: 100), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.favorite_border, size: 48, color: Colors.grey[600]), + const SizedBox(height: 16), + Text( + '还没有收藏歌曲\n在音乐库中点击 ♡ 添加', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.grey[500], + height: 1.6, + ), + ), + ], + ), + ), + ), + ); + } + + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final song = _favorites[index]; + return AnimatedOpacity( + opacity: 1.0, + duration: const Duration(milliseconds: 150), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: ListTile( + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + leading: SongArtwork( + filePath: song.artworkPath, + size: 44, + fallbackIcon: Icons.music_note, + ), + title: Text( + song.displayTitle, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Colors.white, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + song.displaySubtitle, + style: TextStyle(fontSize: 13, color: Colors.grey[400]), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: IconButton( + icon: const Icon( + Icons.favorite, + color: Color(0xFFE05C5C), + size: 20, + ), + onPressed: () async { + await SongDatabase().removeFavorite(song.path); + await _loadFavoritesFromDb(); + if (mounted) safeSetState(() {}); + }, + ), + onTap: () => _playFavorites(index), + ), + ), + ); + }, + childCount: _favorites.length, + ), + ); + } + + Widget _buildPlaylistsSliver() { + if (_playlistSummaries.isEmpty) { + return SliverFillRemaining( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.queue_music, size: 48, color: Colors.grey[600]), + const SizedBox(height: 16), + Text( + '还没有歌单\n在文件列表长按歌曲即可加入', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.grey[500], + height: 1.6, + ), + ), + ], + ), + ), + ); + } + + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final playlist = _playlistSummaries[index]; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: ListTile( + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + leading: Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: const Color(0xFF1E2A28), + borderRadius: BorderRadius.circular(10), + ), + child: const Icon( + Icons.queue_music, + color: Color(0xFFB8D4D0), + size: 22, + ), + ), + title: Text( + playlist.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Colors.white, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + '${playlist.songCount} 首', + style: TextStyle(fontSize: 13, color: Colors.grey[400]), + ), + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (_) => PlaylistDetailPage( + playlistId: playlist.id, + playlistName: playlist.name, + ), + ), + ); + await _loadPlaylistSummaries(); + if (mounted) safeSetState(() {}); + }, + ), + ); + }, + childCount: _playlistSummaries.length, + ), + ); + } + /// 收藏列表整队入队:点哪首就从哪首开始,队列是整个收藏列表 Future _playFavorites(int index) async { if (_favorites.isEmpty) return; @@ -475,7 +700,16 @@ class _HomePageState extends BaseState { ), ), Expanded( - child: CustomScrollView( + child: GestureDetector( + onHorizontalDragEnd: (details) { + final v = details.primaryVelocity ?? 0; + if (v < -200 && _homeTab == 0) { + safeSetState(() => _homeTab = 1); + } else if (v > 200 && _homeTab == 1) { + safeSetState(() => _homeTab = 0); + } + }, + child: CustomScrollView( controller: _scrollController, physics: _mediaLibraryHeight > 0 ? MagneticScrollPhysics( @@ -662,42 +896,6 @@ class _HomePageState extends BaseState { ), ), ), - const SizedBox(width: 16), - Expanded( - child: _ClickableTile( - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const PlaylistsPage(), - ), - ); - if (mounted) safeSetState(() {}); - }, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 10), - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Icon(Icons.playlist_play, - size: 24, - color: const Color(0xFFB8D4D0)), - const SizedBox(width: 8), - const Text( - '歌单列表', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Colors.white70, - ), - ), - ], - ), - ), - ), - ), ], ), const SizedBox(height: 32), @@ -713,19 +911,26 @@ class _HomePageState extends BaseState { height: 48, color: const Color(0xFF0E1211), padding: const EdgeInsets.symmetric(horizontal: 20), - child: const Row( + child: Row( children: [ - Icon(Icons.favorite, - color: Color(0xFFB8D4D0), size: 20), - SizedBox(width: 8), + Icon( + _homeTab == 0 + ? Icons.favorite + : Icons.queue_music, + color: const Color(0xFFB8D4D0), + size: 20, + ), + const SizedBox(width: 8), Text( - '我的收藏', - style: TextStyle( + _homeTab == 0 ? '我的收藏' : '歌单列表', + style: const TextStyle( fontSize: 22, fontWeight: FontWeight.w500, color: Color(0xFFB8D4D0), ), ), + const Spacer(), + _buildHomeTabIndicator(), ], ), ), @@ -737,110 +942,14 @@ class _HomePageState extends BaseState { right: 20, bottom: UIConstants.miniPlayerBottomSpace, ), - sliver: _isLoading - ? const SliverFillRemaining( - child: Center( - child: CircularProgressIndicator( - color: Color(0xFFB8D4D0), - ), - ), - ) - : _favorites.isEmpty - ? SliverFillRemaining( - child: AnimatedOpacity( - opacity: 1.0 - _listReveal * 0.3, - duration: const Duration(milliseconds: 100), - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.favorite_border, - size: 48, - color: Colors.grey[600], - ), - const SizedBox(height: 16), - Text( - '还没有收藏歌曲\n在音乐库中点击 ♡ 添加', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16, - color: Colors.grey[500], - height: 1.6, - ), - ), - ], - ), - ), - ), - ) - : SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - final song = _favorites[index]; - - // 收藏项不做滚动渐显:_listReveal 在首屏为 0, - // 会让条目完全透明(看起来"收藏了但不显示")。 - return AnimatedOpacity( - opacity: 1.0, - duration: const Duration(milliseconds: 150), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 2), - child: ListTile( - contentPadding: EdgeInsets.zero, - visualDensity: VisualDensity.compact, - leading: SongArtwork( - filePath: song.artworkPath, - size: 44, - fallbackIcon: Icons.music_note, - ), - title: Text( - song.displayTitle, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.white, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - song.displaySubtitle, - style: TextStyle( - fontSize: 13, - color: Colors.grey[400], - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: IconButton( - icon: const Icon( - Icons.favorite, - color: Color(0xFFE05C5C), - size: 20, - ), - onPressed: () async { - await SongDatabase() - .removeFavorite(song.path); - await _loadFavoritesFromDb(); - if (mounted) safeSetState(() {}); - }, - ), - onTap: () => _playFavorites(index), - ), - ), - ); - }, - childCount: _favorites.length, - ), - ), + sliver: _buildHomeSliver(), ), ], ), + ), ), ], ), ); } -} \ No newline at end of file +}