Files
UnionApp/lib/pages/home/home_page.dart
spasolreisa 9ce601aa8d initial
2026-04-16 14:26:52 +08:00

431 lines
15 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import '../../tool/gradientText.dart';
// 注意:如果 UserPage, SongListPage 等只是作为内部卡片展示,不需要再 import 用于 Navigator push
// 但如果其他卡片还需要跳转,保留 import 即可
import '../user/userpage.dart';
import '../songlistpage.dart';
import '../scorelist.dart';
import 'package:provider/provider.dart';
import '../../providers/user_provider.dart';
class HomePage extends StatelessWidget {
// ✅ 1. 添加回调函数参数
final Function(int)? onSwitchTab;
const HomePage({super.key, this.onSwitchTab});
@override
Widget build(BuildContext context) {
final userProvider = context.watch<UserProvider>();
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ====================== 顶部 Header ======================
SizedBox(
width: double.infinity,
height: 100,
child: Stack(
children: [
const Padding(
padding: EdgeInsets.only(left: 30, top: 60),
child: Text(
"Tool",
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
),
Positioned(
right: 20,
top: 25,
child: Row(
children: [
GradientText(
data: userProvider.username,
style: const TextStyle(fontSize: 19, fontWeight: FontWeight.bold),
gradientLayers: [
GradientLayer(
gradient: const LinearGradient(
colors: [Colors.pinkAccent, Colors.orangeAccent],
),
blendMode: BlendMode.srcIn,
),
],
),
const SizedBox(width: 8),
ClipRRect(
borderRadius: BorderRadius.circular(0), // 修正语法错误,原代码可能有误
child: SizedBox(
width: 54,
height: 54,
child: userProvider.avatarUrl.isNotEmpty
? Image.network(
userProvider.avatarUrl,
fit: BoxFit.cover,
)
: Container(
color: Colors.grey[200],
child: const Icon(Icons.person, size: 30),
),
),
)
],
),
),
],
),
),
const SizedBox(height: 30),
// ====================== 横向卡片区域 ======================
SizedBox(
height: 180,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 10).copyWith(bottom: 30),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ✅ 2. 修改:用户中心 -> 切换到 Tab 3 (UserPage)
_buildCardWithTitle(
context: context,
title: "用户中心",
icon: Icons.person_outline,
// 不再传递 targetPage而是传递 targetIndex
targetIndex: 3,
gradient: const LinearGradient(
colors: [Colors.black, Colors.grey],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shadowColor: Colors.black38,
),
const SizedBox(width: 20),
// 歌曲列表 (保持原有跳转逻辑或自行定义)
_buildCardWithTitle(
context: context,
title: "歌曲列表",
icon: Icons.music_note_outlined,
targetPage: const SongListPage(), // 假设这个还是用 Push 跳转
gradient: const LinearGradient(
colors: [Color(0xFFff9a9e), Color(0xFFfecfef)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shadowColor: const Color(0xFFff9a9e).withOpacity(0.6),
),
const SizedBox(width: 20),
// ✅ 3. 修改:成绩管理 -> 切换到 Tab 1 (ScorePage)
_buildCardWithTitle(
context: context,
title: "成绩管理",
icon: Icons.score,
targetIndex: 1,
gradient: const LinearGradient(
colors: [Color(0xFF84fab0), Color(0xFF8fd3f4)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shadowColor: const Color(0xFF84fab0).withOpacity(0.6),
),
const SizedBox(width: 20),
// 娱乐功能
_buildCardWithTitle(
context: context,
title: "娱乐功能",
icon: Icons.kebab_dining_sharp,
targetPage: const ScoreListPage(),
gradient: const LinearGradient(
colors: [Colors.lightBlueAccent, Colors.blueAccent],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shadowColor: Colors.lightBlue.withOpacity(0.6),
),
const SizedBox(width: 20),
// 评分列表
_buildCardWithTitle(
context: context,
title: "评分列表",
icon: Icons.star,
targetPage: const ScoreListPage(),
gradient: const LinearGradient(
colors: [Colors.redAccent, Colors.pinkAccent],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shadowColor: Colors.red.withOpacity(0.6),
),
const SizedBox(width: 20),
],
),
),
),
// ====================== 海报 ======================
// 假设 PosterImage 是你自定义的一个 Widget
const PosterImage(imageUrl: 'https://cdn.godserver.cn/post/post%20unionapp1.png'),
const SizedBox(height: 20),
// ====================== 新增:用户数据展示卡片 ======================
const _UserInfoCard(),
const SizedBox(height: 30),
],
),
),
),
);
}
// ✅ 4. 修改构建方法,支持 targetIndex 和 targetPage 两种模式
Widget _buildCardWithTitle({
required BuildContext context,
required String title,
required IconData icon,
int? targetIndex, // 新增:如果是切换 Tab传这个
Widget? targetPage, // 保留:如果是页面跳转,传这个
required LinearGradient gradient,
required Color shadowColor,
}) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
if (targetIndex != null && onSwitchTab != null) {
// ✅ 执行 Tab 切换
onSwitchTab!(targetIndex);
} else if (targetPage != null) {
// ✅ 执行页面跳转
Navigator.push(
context,
MaterialPageRoute(builder: (context) => targetPage),
);
}
},
child: _buildCustomCardBody(
gradient: gradient,
shadowColor: shadowColor,
icon: icon,
),
),
const SizedBox(height: 12),
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
),
],
);
}
Widget _buildCustomCardBody({
required LinearGradient gradient,
required Color shadowColor,
required IconData icon,
}) {
return Container(
width: 100,
height: 100,
decoration: BoxDecoration(
gradient: gradient,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: shadowColor,
blurRadius: 15,
spreadRadius: 3,
offset: const Offset(4, 6),
),
],
),
child: Center(
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.25),
shape: BoxShape.circle,
),
child: Icon(
icon,
size: 36,
color: Colors.white,
),
),
),
);
}
}
// ====================== 新增:用户数据卡片组件 ======================
class _UserInfoCard extends StatelessWidget {
const _UserInfoCard();
@override
Widget build(BuildContext context) {
final userProvider = Provider.of<UserProvider>(context);
return Padding(
// 内边距和海报完全一致,保证同宽
padding: const EdgeInsets.symmetric(horizontal: 17.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white.withOpacity(0.8),
boxShadow: [
BoxShadow(
color: Colors.pink.shade100,
blurRadius: 4,
offset: const Offset(0, 6),
spreadRadius: 1,
),
],
),
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
const Text(
"当前用户信息",
style: TextStyle(
fontSize: 18,
color: Colors.purpleAccent,
fontWeight: FontWeight.bold,
),
),
const Divider(height: 5, thickness: 1),
const SizedBox(height: 6),
// 用户信息行
Row(
children: [
// 头像
ClipRRect(
borderRadius: BorderRadius.circular(0),
child: SizedBox(
width: 60,
height: 60,
child: userProvider.avatarUrl.isNotEmpty
? Image.network(
userProvider.avatarUrl,
fit: BoxFit.cover,
)
: Container(
color: Colors.grey[200],
child: const Icon(Icons.person, size: 30),
),
),
),
const SizedBox(width: 18),
// 文字信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GradientText(
data:"用户名:${userProvider.username}",
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
gradientLayers: [
GradientLayer(
gradient: const LinearGradient(
colors: [Colors.blueAccent, Colors.green],
),
blendMode: BlendMode.srcIn,
),
],
),
const SizedBox(height: 6),
Text(
userProvider.username == "未登录"
? "状态:未登录"
: "状态:已登录",
style: TextStyle(
fontSize: 14,
color: userProvider.username == "未登录"
? Colors.red
: Colors.green,
),
),
],
),
),
],
),
],
),
),
);
}
}
class PosterImage extends StatelessWidget {
final String imageUrl;
const PosterImage({Key? key, required this.imageUrl}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 800,
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.purpleAccent.withOpacity(0.14),
blurRadius: 12,
offset: const Offset(0, 10),
spreadRadius: 10,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
imageUrl,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (context, error, stackTrace) {
return const Center(
child: Icon(Icons.broken_image, size: 50, color: Colors.grey),
);
},
),
),
),
),
);
}
}