diff --git a/.gitignore b/.gitignore index 3820a95..06499a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,45 +1,36 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.build/ -.buildlog/ -.history -.svn/ -.swiftpm/ -migrate_working_dir/ +# ============================================================ +# frpc-console .gitignore +# ============================================================ -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins-dependencies -.pub-cache/ -.pub/ +# ---------- 构建产物 ---------- /build/ -/coverage/ +frpc-console +frpc-console.exe +*.exe +*.test -# Symbolication related -app.*.symbols +# ---------- 运行时生成 ---------- +*.db +*.db-journal +frpc.log +frpc.pid +frpc.toml -# Obfuscation related -app.*.map.json +# ---------- IDE ---------- +.idea/ +.vscode/ +*.swp +*.swo +*~ +.dart_tool +.idea -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release +# ---------- OS 垃圾 ---------- +.DS_Store +Thumbs.db +desktop.ini +1.txt + +# ---------- 临时文件 ---------- +/tmp/ +*.tmp \ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index a80e0b9..6d4c035 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -15,20 +15,20 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "com.example.qt_player" - // You can update the following values to match your application needs. - // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName + + // ⬇️ 把 ndk 配置放在这里(defaultConfig 内部) + ndk { + abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a")) + } } buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. signingConfig = signingConfigs.getByName("debug") } } diff --git a/android/qt_player_android.iml b/android/qt_player_android.iml new file mode 100644 index 0000000..3bc4b3b --- /dev/null +++ b/android/qt_player_android.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index c9640c5..612a461 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -1,54 +1,253 @@ +// lib/pages/home_page.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../services/audio_service.dart'; import '../widgets/mini_player_bar.dart'; -import 'playlist_page.dart'; -import 'settings_page.dart'; -class HomePage extends StatefulWidget { +class HomePage extends StatelessWidget { const HomePage({super.key}); - @override - State createState() => _HomePageState(); -} - -class _HomePageState extends State { - int _currentIndex = 0; - final List _pages = const [ - PlaylistPage(), - SettingsPage(), + // 收藏数据(从你的 ASCII 图提取) + final List> _favorites = const [ + {'title': '暧昧', 'artist': '王菲', 'format': 'flac', 'tag': 'openlist'}, + {'title': '清平调(独唱版)', 'artist': '王菲', 'format': 'flac', 'tag': 'openlist'}, + { + 'title': 'Rain in the Park', + 'artist': 'Marika Takeuchi', + 'format': 'flac', + 'tag': 'openlist' + }, ]; @override Widget build(BuildContext context) { - // 监听播放状态,只要有歌就显示迷你条 final audioService = context.watch(); final showMiniBar = audioService.currentSong != null; return Scaffold( + backgroundColor: const Color(0xFF0E1211), + // ---------- 主体:可滚动内容 ---------- body: Column( children: [ Expanded( - child: IndexedStack( - index: _currentIndex, - children: _pages, + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 48), + // ---------- 顶部标题栏 ---------- + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '清听', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + IconButton( + icon: const Icon(Icons.menu, color: Colors.white54), + onPressed: () { + // 后续:菜单入口 + }, + ), + ], + ), + const SizedBox(height: 28), + + // ---------- 【媒体库】区域 ---------- + const Text( + '媒体库', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Color(0xFFB8D4D0), + ), + ), + const SizedBox(height: 12), + + // ---- WebDAV 连接状态卡片 ---- + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF1A2A2A), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: const Color(0xFF2A4A4A), width: 0.5), + ), + child: Row( + children: [ + const Icon(Icons.cloud_outlined, + color: Color(0xFFB8D4D0), size: 24), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'WebDAV', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.w500), + ), + Text( + 'username@nas', // 后续替换为真实用户名 + style: TextStyle( + fontSize: 13, color: Colors.grey[400]), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xFF4CAF50).withOpacity(0.15), + borderRadius: BorderRadius.circular(20), + ), + child: const Text( + '已连接', + style: TextStyle( + fontSize: 12, color: Color(0xFF4CAF50)), + ), + ), + ], + ), + ), + const SizedBox(height: 20), + + // ---- 三个功能入口(横向三列) ---- + Row( + children: [ + _buildEntryCard(Icons.music_note, '本地音乐'), + const SizedBox(width: 12), + _buildEntryCard(Icons.history, '最近播放'), + const SizedBox(width: 12), + _buildEntryCard(Icons.playlist_play, '歌单列表'), + ], + ), + const SizedBox(height: 32), + + // ---------- 【我的收藏】区域 ---------- + Row( + children: [ + const Icon(Icons.favorite, + color: Color(0xFFB8D4D0), size: 20), + const SizedBox(width: 8), + const Text( + '我的收藏', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Color(0xFFB8D4D0), + ), + ), + const Spacer(), + TextButton( + onPressed: () {}, + child: const Text( + '查看全部', + style: TextStyle(fontSize: 13, color: Colors.grey), + ), + ), + ], + ), + const SizedBox(height: 8), + + // ---- 收藏列表 ---- + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: _favorites.length, + separatorBuilder: (_, __) => const Divider( + color: Colors.white10, + height: 1, + ), + itemBuilder: (context, index) { + final song = _favorites[index]; + return ListTile( + contentPadding: EdgeInsets.zero, + leading: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: const Color(0xFF2A3332), + borderRadius: BorderRadius.circular(6), + ), + child: const Icon(Icons.music_note, + color: Colors.white38, size: 20), + ), + title: Text( + '${song['title']} - ${song['artist']}.${song['format']}', + style: const TextStyle( + fontSize: 15, fontWeight: FontWeight.w400), + ), + subtitle: Text( + song['tag']!, + style: + TextStyle(fontSize: 12, color: Colors.grey[500]), + ), + trailing: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xFF2A4A4A).withOpacity(0.4), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + song['tag']!, + style: const TextStyle( + fontSize: 11, color: Color(0xFF7C9A9E)), + ), + ), + onTap: () { + // 后续:播放该歌曲 + }, + ); + }, + ), + const SizedBox(height: 80), // 底部留空,避免被播放栏遮挡 + ], + ), ), ), - if (showMiniBar) const MiniPlayerBar(), // 底部迷你播放器 - ], - ), - bottomNavigationBar: BottomNavigationBar( - currentIndex: _currentIndex, - backgroundColor: const Color(0xFF1A1F1E), - selectedItemColor: const Color(0xFFB8D4D0), - unselectedItemColor: Colors.grey[600], - onTap: (index) => setState(() => _currentIndex = index), - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.music_note), label: '歌单'), - BottomNavigationBarItem( - icon: Icon(Icons.settings_outlined), label: '设置'), + // ---------- 底部全局播放控制栏(固定) ---------- + if (showMiniBar) const MiniPlayerBar(), ], ), ); } + + // 功能入口卡片(横向三列共用) + Widget _buildEntryCard(IconData icon, String label) { + return Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 14), + decoration: BoxDecoration( + color: const Color(0xFF1A1F1E), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey[800]!, width: 0.5), + ), + child: InkWell( + borderRadius: BorderRadius.circular(12), + onTap: () { + // 后续跳转 + }, + child: Column( + children: [ + Icon(icon, size: 28, color: const Color(0xFFB8D4D0)), + const SizedBox(height: 6), + Text( + label, + style: + const TextStyle(fontSize: 13, fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + ); + } } diff --git a/lib/widgets/mini_player_bar.dart b/lib/widgets/mini_player_bar.dart index b499110..9e1584d 100644 --- a/lib/widgets/mini_player_bar.dart +++ b/lib/widgets/mini_player_bar.dart @@ -1,7 +1,7 @@ +// lib/widgets/mini_player_bar.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../services/audio_service.dart'; -import 'full_player_page.dart'; class MiniPlayerBar extends StatelessWidget { const MiniPlayerBar({super.key}); @@ -11,50 +11,73 @@ class MiniPlayerBar extends StatelessWidget { final service = context.watch(); final song = service.currentSong!; - return GestureDetector( - onTap: () => Navigator.push( - context, - MaterialPageRoute(builder: (_) => const FullPlayerPage()), + return Container( + height: 64, + decoration: BoxDecoration( + color: const Color(0xFF1A1F1E), + border: + const Border(top: BorderSide(color: Colors.white10, width: 0.5)), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 8, + offset: const Offset(0, -2)), + ], ), - child: Container( - height: 64, - decoration: const BoxDecoration( - color: Color(0xFF1A1F1E), - border: Border(top: BorderSide(color: Colors.white10, width: 0.5)), - ), - child: Row( - children: [ - const SizedBox(width: 12), - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: const Color(0xFF2A3332), - borderRadius: BorderRadius.circular(6), - ), - child: const Icon(Icons.music_note, color: Colors.white38), + child: Row( + children: [ + const SizedBox(width: 12), + // ---- 封面方图 ---- + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: const Color(0xFF2A3332), + borderRadius: BorderRadius.circular(6), ), - const SizedBox(width: 12), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(song.title, - style: const TextStyle( - fontSize: 14, fontWeight: FontWeight.w500)), - Text(song.artist, - style: TextStyle(fontSize: 12, color: Colors.grey[400])), - ], - ), + child: + const Icon(Icons.music_note, color: Colors.white38, size: 22), + ), + const SizedBox(width: 12), + // ---- 歌曲信息 ---- + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + song.title, + style: const TextStyle( + fontSize: 14, fontWeight: FontWeight.w500), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + Text( + song.artist, + style: TextStyle(fontSize: 12, color: Colors.grey[400]), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], ), - IconButton( - icon: Icon(service.isPlaying ? Icons.pause : Icons.play_arrow), - onPressed: () => context.read().togglePlay(), + ), + // ---- 控制按钮:播放/暂停 + 展开列表 ---- + IconButton( + icon: Icon( + service.isPlaying ? Icons.pause : Icons.play_arrow, + color: Colors.white, ), - const SizedBox(width: 8), - ], - ), + onPressed: () => context.read().togglePlay(), + ), + IconButton( + icon: + const Icon(Icons.playlist_play_outlined, color: Colors.white54), + onPressed: () { + // 后续:展开当前播放列表 + }, + ), + const SizedBox(width: 4), + ], ), ); } diff --git a/qt_player.iml b/qt_player.iml new file mode 100644 index 0000000..f66303d --- /dev/null +++ b/qt_player.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + +