import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:random_avatar/random_avatar.dart'; import '../../../../core/settings/app_settings.dart'; import '../../../../core/theme/app_theme.dart'; import '../../../../core/theme/ranks.dart'; import '../../../../core/widgets/game_ui.dart'; import '../../../../core/widgets/rank_badge.dart'; import '../../../wallet/presentation/bloc/wallet_bloc.dart'; import '../../../wallet/presentation/bloc/wallet_event.dart'; import '../../domain/entities/profile_entity.dart'; import '../bloc/profile_bloc.dart'; import '../bloc/profile_event.dart'; import '../bloc/profile_state.dart'; import '../bloc/profile_status.dart'; /// صفحه‌ی پروفایل: نام، آواتار (قابل ویرایش)، سطح، جام و آمارِ بازی (ویژه‌ی VIP). class ProfileScreen extends StatelessWidget { const ProfileScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: GameBackground( child: SafeArea( child: BlocConsumer( listenWhen: (a, b) => a.saveStatus != b.saveStatus, listener: (context, state) { final s = state.saveStatus; if (s is ProfileSaveSuccess) { context.read().add(LoadWalletEvent()); } else if (s is ProfileSaveError) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(s.message))); } }, builder: (context, state) { final st = state.loadStatus; if (st is ProfileLoadError) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( st.message, style: const TextStyle(color: Colors.white70), ), const SizedBox(height: 12), GameButton( label: 'تلاش دوباره', onTap: () => context.read().add( LoadProfileEvent(), ), ), ], ), ); } if (st is! ProfileLoadLoaded) { return const Center( child: CircularProgressIndicator(color: AppColors.gold), ); } return _content(context, st.profile); }, ), ), ), ); } Future _editProfile(BuildContext context, ProfileEntity d) async { final result = await showModalBottomSheet>( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => _EditProfileSheet(name: d.name, avatar: d.avatar), ); if (result == null || !context.mounted) return; context.read().add( SaveProfileEvent(result['name']!, result['avatar']!), ); } Widget _content(BuildContext context, ProfileEntity d) { return SingleChildScrollView( padding: const EdgeInsets.fromLTRB(16, 8, 16, 28), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( children: [ const AppBackButton(), const Spacer(), const GlowText('پروفایل', size: 24), const Spacer(), const SizedBox(width: 48), ], ), const SizedBox(height: 12), _HeroCard(profile: d, onEdit: () => _editProfile(context, d)), const SizedBox(height: 14), _HighlightStrip(profile: d), const SizedBox(height: 20), _SectionHeader(icon: Icons.query_stats, title: 'آمار بازی'), const SizedBox(height: 10), _statsSection(context, d), const SizedBox(height: 20), _SectionHeader(icon: Icons.tune, title: 'تنظیمات'), const SizedBox(height: 10), const _GraphicsToggle(), const SizedBox(height: 10), _SettingsTile( icon: Icons.gavel, label: 'شرایط و قوانین', onTap: () => context.push('/terms'), ), ], ), ); } Widget _statsSection(BuildContext context, ProfileEntity d) { final s = d.stats; final rate = (s != null && s.games > 0) ? '${(s.wins * 100 / s.games).round()}٪' : '—'; final tiles = [ _StatTile('نرخ برد', rate, Icons.percent, accent: true), _StatTile('بازی کل', '${s?.games ?? '—'}', Icons.casino), _StatTile('برد', '${s?.wins ?? '—'}', Icons.thumb_up), _StatTile('باخت', '${s?.losses ?? '—'}', Icons.thumb_down), _StatTile('کُت کردن', '${s?.kotMade ?? '—'}', Icons.flash_on), _StatTile('کُت شدن', '${s?.kotReceived ?? '—'}', Icons.flash_off), _StatTile('بریدن', '${s?.cuts ?? '—'}', Icons.bolt), _StatTile('دست حاکم', '${s?.hakemCount ?? '—'}', Icons.workspace_premium), ]; final grid = GridView.count( crossAxisCount: 2, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), mainAxisSpacing: 10, crossAxisSpacing: 10, childAspectRatio: 2.2, children: tiles, ); if (d.vip) return grid; return Stack( children: [ Opacity(opacity: 0.3, child: IgnorePointer(child: grid)), Positioned.fill( child: Container( decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.goldDark), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.lock, color: AppColors.gold, size: 34), const SizedBox(height: 8), const Padding( padding: EdgeInsets.symmetric(horizontal: 24), child: Text( 'مشاهده‌ی آمار ویژه‌ی کاربران VIP است', textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 14), ), ), const SizedBox(height: 12), GameButton( label: 'تهیه اشتراک VIP', icon: Icons.workspace_premium, colors: const [Color(0xFFFFC107), AppColors.goldDark], onTap: () async { await context.push('/vip'); if (context.mounted) { context.read().add(LoadProfileEvent()); } }, ), ], ), ), ), ], ); } } /// کارتِ سرآمدِ پروفایل: آواتار با حلقه‌ی رتبه، نام، رتبه و نوارِ سطح/XP. class _HeroCard extends StatelessWidget { final ProfileEntity profile; final VoidCallback onEdit; const _HeroCard({required this.profile, required this.onEdit}); @override Widget build(BuildContext context) { final d = profile; final grad = rankGradient(d.rankTier); final pct = d.xpNext == 0 ? 0.0 : (d.xpInto / d.xpNext).clamp(0.0, 1.0); return GamePanel( child: Column( children: [ Stack( children: [ Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( colors: grad, begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: grad.first.withValues(alpha: 0.5), blurRadius: 14, spreadRadius: 1, ), ], ), child: Container( padding: const EdgeInsets.all(3), decoration: const BoxDecoration( shape: BoxShape.circle, color: AppColors.bgDark, ), child: RandomAvatar(d.avatar, height: 90, width: 90), ), ), Positioned( bottom: 0, right: 0, child: GestureDetector( onTap: onEdit, child: Container( padding: const EdgeInsets.all(7), decoration: BoxDecoration( shape: BoxShape.circle, gradient: const LinearGradient( colors: [Color(0xFFFFD54F), AppColors.goldDark], ), border: Border.all(color: AppColors.bgDark, width: 2), ), child: const Icon( Icons.edit, color: AppColors.bg, size: 16, ), ), ), ), ], ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Flexible(child: GlowText(d.name, size: 22)), if (d.vip) ...[const SizedBox(width: 8), const _VipBadge()], ], ), const SizedBox(height: 8), RankBadge(tier: d.rankTier, points: d.rankPoints), if (d.mobile.isNotEmpty) ...[ const SizedBox(height: 6), Text( d.mobile, style: const TextStyle(color: Colors.white38, fontSize: 12), ), ], const SizedBox(height: 16), Row( children: [ Text( 'سطح ${d.level}', style: const TextStyle( color: AppColors.gold, fontSize: 13, fontWeight: FontWeight.bold, ), ), const Spacer(), Text( '${d.xpInto} / ${d.xpNext} XP', style: const TextStyle(color: Colors.white38, fontSize: 11), ), ], ), const SizedBox(height: 6), ClipRRect( borderRadius: BorderRadius.circular(6), child: LinearProgressIndicator( value: pct.toDouble(), minHeight: 9, backgroundColor: Colors.white10, valueColor: const AlwaysStoppedAnimation(AppColors.gold), ), ), ], ), ); } } /// نوارِ سه‌گانه‌ی برجسته: سطح، جام، امتیازِ رتبه. class _HighlightStrip extends StatelessWidget { final ProfileEntity profile; const _HighlightStrip({required this.profile}); @override Widget build(BuildContext context) { return Row( children: [ Expanded( child: _Highlight( icon: Icons.star, value: '${profile.level}', label: 'سطح', ), ), const SizedBox(width: 10), Expanded( child: _Highlight( icon: Icons.emoji_events, value: '${profile.trophies}', label: 'جام', ), ), const SizedBox(width: 10), Expanded( child: _Highlight( icon: Icons.military_tech, value: '${profile.rankPoints}', label: 'امتیاز', ), ), ], ); } } class _Highlight extends StatelessWidget { final IconData icon; final String value; final String label; const _Highlight({ required this.icon, required this.value, required this.label, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: AppColors.panel.withValues(alpha: 0.55), borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.goldFaint), ), child: Column( children: [ Icon(icon, color: AppColors.gold, size: 22), const SizedBox(height: 4), Text( value, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( label, style: const TextStyle(color: Colors.white54, fontSize: 11), ), ], ), ); } } /// عنوانِ بخش با آیکون (راست‌چین). class _SectionHeader extends StatelessWidget { final IconData icon; final String title; const _SectionHeader({required this.icon, required this.title}); @override Widget build(BuildContext context) { return Row( children: [ Icon(icon, color: AppColors.gold, size: 18), const SizedBox(width: 8), GlowText(title, size: 18), ], ); } } /// کاشیِ آماری (آیکون + مقدار + برچسب). accent ⇒ برجسته با قابِ طلایی. class _StatTile extends StatelessWidget { final String label; final String value; final IconData icon; final bool accent; const _StatTile(this.label, this.value, this.icon, {this.accent = false}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: accent ? AppColors.gold.withValues(alpha: 0.14) : AppColors.panel.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(14), border: Border.all( color: accent ? AppColors.gold : AppColors.goldFaint, width: accent ? 1.4 : 1, ), ), child: Row( children: [ Icon(icon, color: AppColors.gold, size: 22), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( value, style: const TextStyle( color: Colors.white, fontSize: 17, fontWeight: FontWeight.bold, ), ), Text( label, style: const TextStyle( color: Colors.white54, fontSize: 11, ), ), ], ), ), ], ), ); } } /// ردیفِ تنظیمات (آیکون + برچسب + فلش) با ظاهرِ هماهنگ با سوئیچِ گرافیک. class _SettingsTile extends StatelessWidget { final IconData icon; final String label; final VoidCallback onTap; const _SettingsTile({ required this.icon, required this.label, required this.onTap, }); @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(12), onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14), decoration: BoxDecoration( color: AppColors.panel.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.goldFaint), ), child: Row( children: [ Icon(icon, color: AppColors.gold, size: 20), const SizedBox(width: 10), Expanded( child: Text( label, style: const TextStyle(color: Colors.white, fontSize: 14), ), ), const Icon( Icons.chevron_left, color: Colors.white38, size: 20, ), ], ), ), ), ); } } class _VipBadge extends StatelessWidget { const _VipBadge(); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFFFFD54F), AppColors.goldDark], ), borderRadius: BorderRadius.circular(8), ), child: const Text( 'VIP', style: TextStyle( color: AppColors.bg, fontWeight: FontWeight.bold, fontSize: 12, ), ), ); } } /// شیتِ ویرایش نام و آواتار (با تأیید، مقدار جدید را برمی‌گرداند). class _EditProfileSheet extends StatefulWidget { final String name; final String avatar; const _EditProfileSheet({required this.name, required this.avatar}); @override State<_EditProfileSheet> createState() => _EditProfileSheetState(); } class _EditProfileSheetState extends State<_EditProfileSheet> { late final TextEditingController _name; late final List _seeds; late String _selected; @override void initState() { super.initState(); _name = TextEditingController(text: widget.name); _seeds = List.generate(12, (i) => 'hakem-${i + 1}'); if (!_seeds.contains(widget.avatar)) _seeds.insert(0, widget.avatar); _selected = widget.avatar; } @override void dispose() { _name.dispose(); super.dispose(); } bool get _valid => _name.text.trim().length >= 2; @override Widget build(BuildContext context) { return SafeArea( top: false, child: Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), child: Container( decoration: const BoxDecoration( color: AppColors.bgDark, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), border: Border(top: BorderSide(color: AppColors.gold, width: 2)), ), padding: const EdgeInsets.all(18), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ const GlowText('ویرایش پروفایل', size: 20), const SizedBox(height: 14), Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: AppColors.gold, width: 2), ), child: RandomAvatar(_selected, height: 72, width: 72), ), const SizedBox(height: 12), TextField( controller: _name, textAlign: TextAlign.center, maxLength: 20, inputFormatters: [LengthLimitingTextInputFormatter(20)], decoration: const InputDecoration( hintText: 'نام نمایشی (۲ تا ۲۰ حرف)', counterText: '', ), onChanged: (_) => setState(() {}), ), const SizedBox(height: 10), const Align( alignment: Alignment.centerRight, child: Text( 'انتخاب آواتار', style: TextStyle(color: AppColors.gold), ), ), const SizedBox(height: 8), GridView.count( crossAxisCount: 4, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), mainAxisSpacing: 10, crossAxisSpacing: 10, children: [ for (final s in _seeds) GestureDetector( onTap: () => setState(() => _selected = s), child: Container( decoration: BoxDecoration( shape: BoxShape.circle, color: AppColors.panel, border: Border.all( color: _selected == s ? AppColors.gold : Colors.transparent, width: 2.5, ), ), child: RandomAvatar(s), ), ), ], ), const SizedBox(height: 16), GameButton( label: 'تأیید', width: double.infinity, colors: const [AppColors.success, AppColors.successDark], onTap: _valid ? () => Navigator.pop(context, { 'name': _name.text.trim(), 'avatar': _selected, }) : null, ), ], ), ), ), ), ); } } /// سوئیچِ «گرافیک کم‌مصرف»: افکت‌های اتمسفری را برای گوشی‌های ضعیف خاموش می‌کند. class _GraphicsToggle extends StatelessWidget { const _GraphicsToggle(); @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: AppSettings.I.lowGraphics, builder: (context, low, _) => Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), decoration: BoxDecoration( color: AppColors.panel.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.goldFaint), ), child: Row( children: [ const Icon(Icons.bolt, color: AppColors.gold, size: 20), const SizedBox(width: 10), const Expanded( child: Text( 'گرافیک کم‌مصرف', style: TextStyle(color: Colors.white, fontSize: 14), ), ), Switch( value: low, activeThumbColor: AppColors.gold, onChanged: (v) => AppSettings.I.setLowGraphics(v), ), ], ), ), ); } }