ver1.00.00

update
This commit is contained in:
spasolreisa
2026-04-21 00:28:41 +08:00
parent b985cd1f9e
commit f5f62c828d
13 changed files with 1496 additions and 175 deletions

View File

@@ -2,13 +2,17 @@ import 'package:flutter/material.dart';
import 'package:unionapp/pages/music/music_page.dart';
import '../../service/recommendation_helper.dart';
import '../../service/song_service.dart';
import '../../tool/cacheImage.dart';
import '../../tool/gradientText.dart';
import '../music/adx.dart';
import '../music/score_single.dart';
import '../score/updateScorePage.dart';
import '../user/userpage.dart';
import '../scorelist.dart';
import 'package:provider/provider.dart';
import '../../providers/user_provider.dart';
import '../../model/song_model.dart';
import 'package:url_launcher/url_launcher.dart';
class HomePage extends StatelessWidget {
final Function(int)? onSwitchTab;
@@ -177,7 +181,7 @@ class HomePage extends StatelessWidget {
children: [
// 左侧:智能推荐乐曲(占 6 份宽度)
Expanded(
flex: 6,
flex: 7,
child: const _RecommendedSongsSection(),
),
const SizedBox(width: 4),
@@ -249,7 +253,14 @@ class HomePage extends StatelessWidget {
offset: const Offset(2, 4),
),
],
onTap: () {},
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => AdxDownloadGridPage(),
),
);
},
),
// 橙色渐变
@@ -269,7 +280,27 @@ class HomePage extends StatelessWidget {
offset: const Offset(2, 4),
),
],
onTap: () {},
onTap: () async {
// 你的固定分享链接
const url = "https://bot.q.qq.com/s/c2mloqdgv?id=102172520";
const title = "ReiSasol";
// QQ 分享 Scheme
final qqScheme = 'mqq://im/chat?chat_type=wpa&url=${Uri.encodeComponent(url)}&title=${Uri.encodeComponent(title)}';
final uri = Uri.parse(qqScheme);
try {
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
// 未安装QQ → 用浏览器打开链接
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
}
} catch (e) {
// 兜底打开
await launchUrl(Uri.parse(url));
}
},
),
_quickActionItem(
icon: Icons.link,
@@ -452,7 +483,7 @@ class _UserInfoCard extends StatelessWidget {
width: 60,
height: 60,
child: userProvider.avatarUrl.isNotEmpty
? Image.network(
? CacheImage.network(
userProvider.avatarUrl,
fit: BoxFit.cover,
)
@@ -621,7 +652,6 @@ class _SongItemCard extends StatelessWidget {
String _getCoverUrl(int musicId) {
int displayId = musicId % 10000;
// 注意:这里逻辑可能需要根据你的实际资源调整,通常 DX 歌曲 ID > 10000
if (musicId >= 10000) {
String idStr = displayId.toString().padLeft(6, '0');
return "https://u.mai2.link/jacket/UI_Jacket_$idStr.jpg";
@@ -630,116 +660,117 @@ class _SongItemCard extends StatelessWidget {
}
}
// 获取难度颜色
Color _getLevelColor(int levelIndex) {
switch (levelIndex) {
case 0: return Colors.green; // Basic
case 1: return Colors.blue; // Advanced
case 2: return Colors.yellow[700]!; // Expert
case 3: return Colors.red; // Master
case 4: return Colors.purple; // Re:Master
case 0: return Colors.green;
case 1: return Colors.blue;
case 2: return Colors.yellow[700]!;
case 3: return Colors.red;
case 4: return Colors.purple;
default: return Colors.grey;
}
}
@override
Widget build(BuildContext context) {
// 假设我们要显示 Master (3) 和 Re:Master (4) 的难度
// 你需要从 song.sd 或 song.dx 中解析出具体的 level_value (定数)
double? masterLv = _getLevelValue(song, 3);
double? reMasterLv = _getLevelValue(song, 4);
return Container(
width: 140,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.15),
blurRadius: 8,
offset: const Offset(0, 4),
// ⭐ 核心:用 InkWell / GestureDetector 包裹整个卡片,实现点击跳转
return InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () {
// 跳转到歌曲详情页
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SongDetailPage(
song: song,
userScoreCache: {}, // 你可以根据实际页面传值
),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ✅ 1. 图片优化:增加 loadingBuilder 和 cacheWidth
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: SizedBox(
height: 140,
width: double.infinity,
child: Image.network(
_getCoverUrl(song.id),
fit: BoxFit.cover,
// 关键优化:指定缓存宽度,减少内存占用和解码时间
cacheWidth: 280,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.grey[200],
child: Center(child: CircularProgressIndicator(
strokeWidth: 2,
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
)),
);
},
errorBuilder: (_, __, ___) {
return Container(
color: Colors.grey[200],
child: const Icon(Icons.music_note, size: 40, color: Colors.grey),
);
},
);
},
child: Container(
width: 140,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.15),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: SizedBox(
height: 140,
width: double.infinity,
child: CacheImage.network(
_getCoverUrl(song.id),
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.grey[200],
child: Center(child: CircularProgressIndicator(
strokeWidth: 2,
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
)),
);
},
),
),
),
),
const SizedBox(height: 8),
const SizedBox(height: 8),
// 标题
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
song.title ?? "未知歌曲",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
song.title ?? "未知歌曲",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
),
),
// 艺术家 (修复了重复显示的问题)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
song.artist ?? "未知艺术家",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 11, color: Colors.grey[600]),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
song.artist ?? "未知艺术家",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 11, color: Colors.grey[600]),
),
),
),
const SizedBox(height: 6),
const SizedBox(height: 6),
// ✅ 2. 底部难度标签
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
if (masterLv != null)
_buildLevelTag("MAS", masterLv, Colors.purple),
if (reMasterLv != null) ...[
const SizedBox(width: 4),
_buildLevelTag("ReM", reMasterLv, Colors.deepPurple),
]
],
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
if (masterLv != null)
_buildLevelTag("MAS", masterLv, Colors.purple),
if (reMasterLv != null) ...[
const SizedBox(width: 4),
_buildLevelTag("ReM", reMasterLv, Colors.deepPurple),
]
],
),
),
),
],
],
),
),
);
}
@@ -763,18 +794,17 @@ class _SongItemCard extends StatelessWidget {
);
}
// 辅助方法:获取定数
double? _getLevelValue(SongModel song, int levelIndex) {
Map ?diffMap = song.dx;
if (diffMap==null|| diffMap.isEmpty) {
Map? diffMap = song.dx;
if (diffMap == null || diffMap.isEmpty) {
diffMap = song.sd;
}
var data = diffMap?["$levelIndex"] ?? diffMap?[levelIndex];
if (data is Map && data["level_value"] != null) {
return (data["level_value"] as num).toDouble();
}
return null;
var data = diffMap?["$levelIndex"] ?? diffMap?[levelIndex];
if (data is Map && data["level_value"] != null) {
return (data["level_value"] as num).toDouble();
}
return null;
}
}
@@ -804,7 +834,7 @@ class PosterImage extends StatelessWidget {
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
child: CacheImage.network(
imageUrl,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {