Files
front-hokm/lib/feature/wallet/presentation/screen/lobby_screen.dart
T
2026-07-08 00:17:46 +03:30

620 lines
22 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:random_avatar/random_avatar.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/widgets/game_ui.dart';
import '../../../auth/presentation/bloc/auth_bloc.dart';
import '../../../auth/presentation/bloc/auth_event.dart';
import '../../domain/entities/wallet_entity.dart';
import '../bloc/daily_status.dart';
import '../bloc/wallet_bloc.dart';
import '../bloc/wallet_event.dart';
import '../bloc/wallet_state.dart';
import '../bloc/wallet_status.dart';
import '../widgets/streak_dialog.dart';
/// لابی اصلی: کیف‌پول، دکمه بازی/دورهمی/فروشگاه/سکه روزانه.
class LobbyScreen extends StatefulWidget {
const LobbyScreen({super.key});
@override
State<LobbyScreen> createState() => _LobbyScreenState();
}
class _LobbyScreenState extends State<LobbyScreen> {
@override
void initState() {
super.initState();
context.read<WalletBloc>().add(LoadWalletEvent());
}
void _reload() => context.read<WalletBloc>().add(LoadWalletEvent());
@override
Widget build(BuildContext context) {
return Scaffold(
body: GameBackground(
child: BlocConsumer<WalletBloc, WalletBlocState>(
listenWhen: (a, b) => a.dailyStatus != b.dailyStatus,
listener: (context, state) {
final d = state.dailyStatus;
if (d is DailySuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('سکه روزانه دریافت شد: +${d.amount}')),
);
} else if (d is DailyError) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(d.message)));
}
},
builder: (context, state) {
final st = state.walletStatus;
final wallet = st is WalletLoaded ? st.wallet : null;
return SafeArea(
child: Column(
children: [
_TopBar(wallet: wallet, onCoinTap: () => _openShop(context)),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(16, 6, 16, 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 6),
const _LobbyHeader(),
const SizedBox(height: 18),
// اکشنِ اصلی: بازیِ سریع.
_PrimaryPlay(
onTap: () async {
await context.push('/game/tiers');
if (context.mounted) _reload();
},
),
const SizedBox(height: 14),
// اکشن‌های ثانویه در گریدِ دوستونه.
GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 1.55,
children: [
_ActionTile(
icon: Icons.group_add,
label: 'دورهمی',
sub: 'بازی با دوستان',
accent: AppColors.accent,
onTap: () async {
await context.push('/private');
if (context.mounted) _reload();
},
),
_ActionTile(
icon: Icons.emoji_events,
label: 'تورنومنت',
sub: 'جایزه بگیر',
accent: const Color(0xFF9B7BE0),
onTap: () async {
await context.push('/tournaments');
if (context.mounted) _reload();
},
),
_ActionTile(
icon: Icons.leaderboard,
label: 'رتبه‌بندی',
sub: 'جدول قهرمانان',
accent: AppColors.gold,
onTap: () => context.push('/leaderboard'),
),
_ActionTile(
icon: Icons.storefront,
label: 'فروشگاه',
sub: 'سکه و آیتم',
accent: AppColors.success,
onTap: () => _openShop(context),
),
],
),
const SizedBox(height: 14),
_DailyCard(
wallet: wallet,
onTap:
() => StreakDialog.show(
context,
streak: wallet?.dailyStreak ?? 0,
best: wallet?.bestStreak ?? 0,
ready: wallet?.dailyReady ?? true,
nextReward: wallet?.nextDaily ?? 200,
onClaim:
() => context.read<WalletBloc>().add(
ClaimDailyEvent(),
),
),
),
],
),
),
),
TextButton.icon(
onPressed: () {
context.read<AuthBloc>().add(LogoutEvent());
context.go('/login');
},
icon: const Icon(
Icons.logout,
color: Colors.white38,
size: 18,
),
label: const Text(
'خروج از حساب',
style: TextStyle(color: Colors.white38, fontSize: 13),
),
),
],
),
);
},
),
),
);
}
Future<void> _openShop(BuildContext context) async {
await context.push('/shop');
if (context.mounted) _reload();
}
}
/// عنوانِ لابی: تاجِ طلایی + نامِ بازی + زیرعنوان.
class _LobbyHeader extends StatelessWidget {
const _LobbyHeader();
@override
Widget build(BuildContext context) {
return Column(
children: const [
Icon(Icons.workspace_premium, color: AppColors.gold, size: 34),
SizedBox(height: 4),
GlowText('حاکم ایرانی', size: 36),
SizedBox(height: 2),
Text(
'بازیِ آنلاینِ حکم',
style: TextStyle(color: Colors.white54, fontSize: 13),
),
],
);
}
}
/// اکشنِ اصلی: بازیِ سریع (میزِ عمومی) — کارتِ بزرگِ برجسته.
class _PrimaryPlay extends StatelessWidget {
final VoidCallback onTap;
const _PrimaryPlay({required this.onTap});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 18),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.lapis, AppColors.lapisHi, AppColors.lapisLo],
),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColors.gold, width: 2),
boxShadow: [
BoxShadow(
color: AppColors.gold.withValues(alpha: 0.28),
blurRadius: 16,
),
const BoxShadow(
color: Colors.black54,
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: Row(
children: [
Container(
width: 54,
height: 54,
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [AppColors.gold, AppColors.goldDark],
),
shape: BoxShape.circle,
),
child: const Icon(
Icons.play_arrow_rounded,
color: AppColors.lapisInk,
size: 36,
),
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'بازی سریع',
style: TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 2),
Text(
'ورود به میزِ عمومی',
style: TextStyle(color: Colors.white70, fontSize: 13),
),
],
),
const Spacer(),
const Icon(
Icons.arrow_forward_ios,
color: AppColors.gold,
size: 18,
),
],
),
),
);
}
}
/// کاشیِ اکشنِ ثانویه در گرید (آیکن + عنوان + زیرعنوان).
class _ActionTile extends StatelessWidget {
final IconData icon;
final String label;
final String sub;
final Color accent;
final VoidCallback onTap;
const _ActionTile({
required this.icon,
required this.label,
required this.sub,
required this.accent,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(18),
child: InkWell(
borderRadius: BorderRadius.circular(18),
onTap: onTap,
child: Ink(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColors.lapisHi, AppColors.lapisLo],
),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: AppColors.goldDark, width: 1.2),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(9),
decoration: BoxDecoration(
color: accent.withValues(alpha: 0.18),
shape: BoxShape.circle,
border: Border.all(color: accent.withValues(alpha: 0.6)),
),
child: Icon(icon, color: accent, size: 22),
),
const Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
label,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Text(
sub,
style: const TextStyle(color: Colors.white54, fontSize: 11),
),
],
),
],
),
),
),
);
}
}
/// کارتِ هدیه‌ی روزانه با استریک (مثلِ دولینگو): شعله + تعدادِ روزهای پیاپی و
/// وضعیتِ قابلِ‌دریافت/دریافت‌شده.
class _DailyCard extends StatelessWidget {
final WalletEntity? wallet;
final VoidCallback onTap;
const _DailyCard({required this.wallet, required this.onTap});
@override
Widget build(BuildContext context) {
final w = wallet;
final streak = w?.dailyStreak ?? 0;
final ready = w?.dailyReady ?? true;
final next = w?.nextDaily ?? 200;
final colors =
ready
? const [AppColors.success, AppColors.successDark]
: const [AppColors.lapisHi, AppColors.lapisLo];
return Opacity(
opacity: ready ? 1 : 0.75,
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(16),
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: onTap, // همیشه دیالوگِ استریک را باز می‌کند
child: Ink(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 13),
decoration: BoxDecoration(
gradient: LinearGradient(colors: colors),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.gold, width: 1.4),
),
child: Row(
children: [
// شعله‌ی استریک با عددِ روز.
Stack(
alignment: Alignment.center,
children: [
Icon(
Icons.local_fire_department,
color:
streak > 0 ? const Color(0xFFFF7A1A) : Colors.white54,
size: 40,
),
if (streak > 0)
Padding(
padding: const EdgeInsets.only(top: 6),
child: Text(
'$streak',
style: const TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'هدیه‌ی روزانه',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
streak > 0
? '$streak روز پیاپی 🔥'
: 'زنجیره‌ات رو شروع کن!',
style: const TextStyle(
color: Colors.white70,
fontSize: 12,
),
),
],
),
const Spacer(),
if (ready)
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.monetization_on,
color: AppColors.gold,
size: 16,
),
const SizedBox(width: 3),
Text(
'+$next',
style: const TextStyle(
color: AppColors.gold,
fontWeight: FontWeight.bold,
),
),
],
),
const Text(
'دریافت',
style: TextStyle(color: Colors.white70, fontSize: 11),
),
],
)
else
const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check_circle, color: AppColors.gold, size: 18),
SizedBox(width: 4),
Text(
'امروز گرفتی',
style: TextStyle(color: Colors.white70, fontSize: 12),
),
],
),
],
),
),
),
),
);
}
}
class _TopBar extends StatelessWidget {
final WalletEntity? wallet;
final VoidCallback onCoinTap;
const _TopBar({required this.wallet, required this.onCoinTap});
@override
Widget build(BuildContext context) {
final w = wallet;
return Container(
margin: const EdgeInsets.all(8),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColors.bg, AppColors.bgDark],
),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.goldDark),
boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 8)],
),
child: Row(
children: [
GestureDetector(
onTap: () async {
await context.push('/profile');
if (context.mounted) {
context.read<WalletBloc>().add(LoadWalletEvent());
}
},
child: Container(
width: 48,
height: 48,
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.panel,
border: Border.all(color: AppColors.gold, width: 2),
),
child:
(w == null || w.avatar.isEmpty)
? const Icon(Icons.person, color: AppColors.gold)
: ClipOval(child: RandomAvatar(w.avatar)),
),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
if (w != null && w.name.isNotEmpty) ...[
Text(
w.name,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 6),
],
if (w?.vip == true) const _VipTag(),
],
),
const SizedBox(height: 2),
Row(
children: [
const Icon(Icons.star, color: AppColors.gold, size: 16),
const SizedBox(width: 4),
Text(
'سطح ${w?.level ?? '-'}',
style: const TextStyle(
color: AppColors.gold,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 4),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: SizedBox(
width: 96,
height: 7,
child: LinearProgressIndicator(
value:
(w == null || w.xpForNext == 0)
? 0
: w.xpIntoLevel / w.xpForNext,
backgroundColor: Colors.white10,
valueColor: const AlwaysStoppedAnimation(AppColors.gold),
),
),
),
],
),
const Spacer(),
GestureDetector(
onTap: onCoinTap,
child: StatChip(
icon: Icons.confirmation_number,
value: '${w?.tickets ?? 0}',
),
),
const SizedBox(width: 8),
GestureDetector(
onTap: onCoinTap,
child: StatChip(
icon: Icons.monetization_on,
value: '${w?.coins ?? 0}',
),
),
],
),
);
}
}
class _VipTag extends StatelessWidget {
const _VipTag();
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFFFFD54F), AppColors.goldDark],
),
borderRadius: BorderRadius.circular(6),
),
child: const Text(
'VIP',
style: TextStyle(
color: AppColors.bg,
fontWeight: FontWeight.bold,
fontSize: 10,
),
),
);
}
}