357 lines
13 KiB
Dart
357 lines
13 KiB
Dart
import 'package:flutter/material.dart';
|
||
import '../../../../app/theme/app_colors.dart';
|
||
import '../../../../app/theme/app_typography.dart';
|
||
import '../../../../app/theme/app_spacing.dart';
|
||
import '../../../../shared/widgets/coupon_card.dart';
|
||
import '../../../ai_agent/presentation/widgets/ai_fab.dart';
|
||
import '../widgets/receive_coupon_sheet.dart';
|
||
|
||
/// 首页 - 轻量持仓卡 + 分类网格 + AI推荐 + 精选券
|
||
///
|
||
/// Tab导航:首页/交易/消息/我的
|
||
class HomePage extends StatelessWidget {
|
||
const HomePage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
body: Stack(
|
||
children: [
|
||
CustomScrollView(
|
||
slivers: [
|
||
// Floating App Bar
|
||
SliverAppBar(
|
||
floating: true,
|
||
pinned: false,
|
||
backgroundColor: AppColors.background,
|
||
elevation: 0,
|
||
toolbarHeight: 60,
|
||
title: _buildSearchBar(context),
|
||
actions: [
|
||
IconButton(
|
||
icon: const Icon(Icons.qr_code_scanner_rounded, size: 24),
|
||
onPressed: () {},
|
||
color: AppColors.textPrimary,
|
||
),
|
||
],
|
||
),
|
||
|
||
// Lightweight Position Card (持仓)
|
||
SliverToBoxAdapter(child: _buildWalletCard(context)),
|
||
|
||
// Category Grid (8 items, 4x2)
|
||
SliverToBoxAdapter(child: _buildCategoryGrid()),
|
||
|
||
// AI Smart Suggestions
|
||
SliverToBoxAdapter(child: _buildAiSuggestions()),
|
||
|
||
// Section: Featured Coupons
|
||
SliverToBoxAdapter(
|
||
child: Padding(
|
||
padding: const EdgeInsets.fromLTRB(20, 24, 20, 12),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text('精选好券', style: AppTypography.h2),
|
||
GestureDetector(
|
||
onTap: () {},
|
||
child: Text('查看全部', style: AppTypography.labelSmall.copyWith(
|
||
color: AppColors.primary,
|
||
)),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
|
||
// Coupon List
|
||
SliverPadding(
|
||
padding: AppSpacing.pagePadding,
|
||
sliver: SliverList(
|
||
delegate: SliverChildBuilderDelegate(
|
||
(context, index) => Padding(
|
||
padding: const EdgeInsets.only(bottom: 12),
|
||
child: CouponCard(
|
||
brandName: _mockBrands[index % _mockBrands.length],
|
||
couponName: _mockNames[index % _mockNames.length],
|
||
faceValue: _mockFaceValues[index % _mockFaceValues.length],
|
||
currentPrice: _mockPrices[index % _mockPrices.length],
|
||
creditRating: _mockRatings[index % _mockRatings.length],
|
||
expiryDate: DateTime.now().add(Duration(days: (index + 1) * 5)),
|
||
onTap: () {
|
||
Navigator.pushNamed(context, '/coupon/detail');
|
||
},
|
||
),
|
||
),
|
||
childCount: 10,
|
||
),
|
||
),
|
||
),
|
||
|
||
const SliverPadding(padding: EdgeInsets.only(bottom: 100)),
|
||
],
|
||
),
|
||
|
||
// AI FAB
|
||
Positioned(
|
||
right: 20,
|
||
bottom: 100,
|
||
child: AiFab(
|
||
unreadCount: 3,
|
||
onTap: () {
|
||
Navigator.pushNamed(context, '/ai-chat');
|
||
},
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildSearchBar(BuildContext context) {
|
||
return GestureDetector(
|
||
onTap: () {
|
||
Navigator.pushNamed(context, '/search');
|
||
},
|
||
child: Container(
|
||
height: 40,
|
||
decoration: BoxDecoration(
|
||
color: AppColors.gray50,
|
||
borderRadius: AppSpacing.borderRadiusFull,
|
||
border: Border.all(color: AppColors.borderLight),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
const SizedBox(width: 14),
|
||
const Icon(Icons.search_rounded, size: 20, color: AppColors.textTertiary),
|
||
const SizedBox(width: 8),
|
||
Text(
|
||
'搜索券、品牌、分类...',
|
||
style: AppTypography.bodyMedium.copyWith(color: AppColors.textTertiary),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// Lightweight Position Card (持仓卡片)
|
||
// 点击非快捷入口区域打开完整持仓页面
|
||
// ============================================================
|
||
Widget _buildWalletCard(BuildContext context) {
|
||
return GestureDetector(
|
||
onTap: () => Navigator.pushNamed(context, '/wallet/coupons'),
|
||
child: Container(
|
||
margin: const EdgeInsets.fromLTRB(20, 8, 20, 0),
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
gradient: AppColors.cardGradient,
|
||
borderRadius: AppSpacing.borderRadiusLg,
|
||
boxShadow: AppSpacing.shadowPrimary,
|
||
),
|
||
child: Column(
|
||
children: [
|
||
// Top row: wallet info + receive button
|
||
Row(
|
||
children: [
|
||
const Icon(Icons.inventory_2_rounded,
|
||
size: 20, color: Colors.white),
|
||
const SizedBox(width: 8),
|
||
Text('持仓',
|
||
style: AppTypography.labelMedium.copyWith(color: Colors.white)),
|
||
const Spacer(),
|
||
// Summary
|
||
Text('持有 ',
|
||
style: AppTypography.bodySmall.copyWith(
|
||
color: Colors.white.withValues(alpha: 0.7),
|
||
)),
|
||
Text('4',
|
||
style: AppTypography.h3.copyWith(
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w700,
|
||
)),
|
||
Text(' 张券 总值 ',
|
||
style: AppTypography.bodySmall.copyWith(
|
||
color: Colors.white.withValues(alpha: 0.7),
|
||
)),
|
||
Text('\$235',
|
||
style: AppTypography.h3.copyWith(
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w700,
|
||
)),
|
||
const SizedBox(width: 4),
|
||
Icon(Icons.chevron_right_rounded,
|
||
size: 18, color: Colors.white.withValues(alpha: 0.7)),
|
||
],
|
||
),
|
||
|
||
const SizedBox(height: 14),
|
||
|
||
// Quick action entries
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||
children: [
|
||
_buildQuickAction(context, Icons.qr_code_rounded, '接收', () {
|
||
showModalBottomSheet(
|
||
context: context,
|
||
isScrollControlled: true,
|
||
backgroundColor: Colors.transparent,
|
||
builder: (_) => const ReceiveCouponSheet(),
|
||
);
|
||
}),
|
||
_buildQuickAction(context, Icons.card_giftcard_rounded, '转赠', () {
|
||
Navigator.pushNamed(context, '/transfer');
|
||
}),
|
||
_buildQuickAction(context, Icons.sell_rounded, '出售', () {
|
||
Navigator.pushNamed(context, '/sell');
|
||
}),
|
||
_buildQuickAction(context, Icons.check_circle_outline_rounded, '核销', () {
|
||
Navigator.pushNamed(context, '/redeem');
|
||
}),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildQuickAction(
|
||
BuildContext context, IconData icon, String label, VoidCallback onTap) {
|
||
return GestureDetector(
|
||
onTap: onTap,
|
||
behavior: HitTestBehavior.opaque,
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Container(
|
||
width: 40,
|
||
height: 40,
|
||
decoration: BoxDecoration(
|
||
color: Colors.white.withValues(alpha: 0.15),
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
),
|
||
child: Icon(icon, size: 20, color: Colors.white),
|
||
),
|
||
const SizedBox(height: 6),
|
||
Text(
|
||
label,
|
||
style: AppTypography.caption.copyWith(
|
||
color: Colors.white.withValues(alpha: 0.9),
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// Category Grid (8 items, 4x2, A+C savings-focused)
|
||
// ============================================================
|
||
Widget _buildCategoryGrid() {
|
||
final categories = [
|
||
('限时抢购', Icons.flash_on_rounded, AppColors.error),
|
||
('新券首发', Icons.fiber_new_rounded, AppColors.primary),
|
||
('折扣排行', Icons.trending_up_rounded, AppColors.couponEntertainment),
|
||
('即将到期', Icons.timer_rounded, AppColors.warning),
|
||
('比价', Icons.compare_arrows_rounded, AppColors.couponShopping),
|
||
('转让市场', Icons.swap_horiz_rounded, AppColors.info),
|
||
('热门交易', Icons.local_fire_department_rounded, AppColors.couponDining),
|
||
('全部', Icons.grid_view_rounded, AppColors.textSecondary),
|
||
];
|
||
|
||
return Padding(
|
||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 0),
|
||
child: GridView.builder(
|
||
shrinkWrap: true,
|
||
physics: const NeverScrollableScrollPhysics(),
|
||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||
crossAxisCount: 4,
|
||
mainAxisSpacing: 4,
|
||
crossAxisSpacing: 4,
|
||
childAspectRatio: 0.9,
|
||
),
|
||
itemCount: categories.length,
|
||
itemBuilder: (context, index) {
|
||
final (name, icon, color) = categories[index];
|
||
return GestureDetector(
|
||
onTap: () {},
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Container(
|
||
width: 44,
|
||
height: 44,
|
||
decoration: BoxDecoration(
|
||
color: color.withValues(alpha: 0.1),
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
),
|
||
child: Icon(icon, color: color, size: 22),
|
||
),
|
||
const SizedBox(height: 6),
|
||
Text(name, style: AppTypography.caption.copyWith(
|
||
color: AppColors.textPrimary,
|
||
fontWeight: FontWeight.w500,
|
||
)),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildAiSuggestions() {
|
||
return Container(
|
||
margin: const EdgeInsets.fromLTRB(20, 16, 20, 0),
|
||
padding: const EdgeInsets.all(14),
|
||
decoration: BoxDecoration(
|
||
color: AppColors.primarySurface,
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
border: Border.all(color: AppColors.primary.withValues(alpha: 0.15)),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
width: 32,
|
||
height: 32,
|
||
decoration: BoxDecoration(
|
||
gradient: AppColors.primaryGradient,
|
||
borderRadius: AppSpacing.borderRadiusSm,
|
||
),
|
||
child: const Icon(Icons.auto_awesome_rounded, color: Colors.white, size: 16),
|
||
),
|
||
const SizedBox(width: 10),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text('AI 推荐', style: AppTypography.labelSmall.copyWith(
|
||
color: AppColors.primary,
|
||
)),
|
||
const SizedBox(height: 2),
|
||
Text(
|
||
'根据你的偏好,发现了3张高性价比券',
|
||
style: AppTypography.bodySmall,
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
const Icon(Icons.chevron_right_rounded, color: AppColors.primary, size: 20),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// Mock data
|
||
const _mockBrands = ['Starbucks', 'Amazon', 'Walmart', 'Target', 'Nike'];
|
||
const _mockNames = ['星巴克 \$25 礼品卡', 'Amazon \$100 购物券', 'Walmart \$50 生活券', 'Target \$30 折扣券', 'Nike \$80 运动券'];
|
||
const _mockFaceValues = [25.0, 100.0, 50.0, 30.0, 80.0];
|
||
const _mockPrices = [21.25, 85.0, 42.5, 24.0, 68.0];
|
||
const _mockRatings = ['AAA', 'AA', 'AAA', 'A', 'AA'];
|