ver1.00.00
update
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user