feat: phase 1 of improve

This commit is contained in:
2026-07-08 00:17:46 +03:30
parent 0bd2b4a18a
commit ffd2646eea
6 changed files with 461 additions and 135 deletions
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flame/game.dart'; import 'package:flame/game.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show HapticFeedback;
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:random_avatar/random_avatar.dart'; import 'package:random_avatar/random_avatar.dart';
@@ -20,6 +21,7 @@ import '../../domain/entities/game_entities.dart';
import '../bloc/game_bloc.dart'; import '../bloc/game_bloc.dart';
import '../bloc/game_state.dart'; import '../bloc/game_state.dart';
import '../../../chat/presentation/widgets/chat_panel.dart'; import '../../../chat/presentation/widgets/chat_panel.dart';
import '../widgets/confetti.dart';
import '../widgets/flame/hokm_game.dart'; import '../widgets/flame/hokm_game.dart';
import '../widgets/table_hud.dart'; import '../widgets/table_hud.dart';
@@ -94,7 +96,10 @@ class _GameScreenState extends State<GameScreen> {
final won = final won =
state.gameOver!.winnerTeam == state.gameOver!.winnerTeam ==
(state.state?.yourSeat ?? 0) % 2; (state.state?.yourSeat ?? 0) % 2;
if (won) AppSounds.win(); if (won) {
AppSounds.win();
HapticFeedback.mediumImpact();
}
} }
if (state.notice != null) { if (state.notice != null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@@ -619,49 +624,82 @@ class _GameScreenState extends State<GameScreen> {
final g = s.gameOver!; final g = s.gameOver!;
final mySeat = s.state?.yourSeat ?? 0; final mySeat = s.state?.yourSeat ?? 0;
final won = g.winnerTeam == mySeat % 2; final won = g.winnerTeam == mySeat % 2;
return Container( return Stack(
color: Colors.black87, children: [
alignment: Alignment.center, const Positioned.fill(child: ColoredBox(color: Colors.black87)),
if (won) const Positioned.fill(child: Confetti()),
Center(
child: SingleChildScrollView(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// مدالِ متحرک (تاجِ طلایی برای برد، نشانِ ملایم برای باخت).
TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1),
duration: const Duration(milliseconds: 650),
curve: Curves.easeOutBack,
builder: (_, v, __) => Transform.scale(
scale: v.clamp(0.0, 1.2),
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: won
? const [AppColors.gold, AppColors.goldDark]
: [Colors.grey.shade700, Colors.grey.shade900],
),
border: Border.all(
color: won ? Colors.white70 : Colors.white24,
width: 2),
boxShadow: won
? [
BoxShadow(
color: AppColors.gold
.withValues(alpha: 0.55),
blurRadius: 28),
]
: null,
),
child: Icon(
won
? Icons.emoji_events
: Icons.sentiment_dissatisfied_outlined,
color: won ? AppColors.lapisInk : Colors.white54,
size: 54,
),
),
),
),
const SizedBox(height: 16),
Text( Text(
won ? 'بردید! 🎉' : 'باختید', won ? 'بردید! 🎉' : 'باختید',
style: TextStyle( style: TextStyle(
color: won ? AppColors.gold : Colors.redAccent, color: won ? AppColors.gold : Colors.redAccent,
fontSize: 32, fontSize: 34,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
shadows: const [Shadow(color: Colors.black, blurRadius: 8)],
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 10),
Text( Text(
'نتیجه نهایی: ${g.scores.isNotEmpty ? g.scores[0] : 0} - ${g.scores.length > 1 ? g.scores[1] : 0}', 'نتیجه نهایی: ${g.scores.isNotEmpty ? g.scores[0] : 0} - ${g.scores.length > 1 ? g.scores[1] : 0}',
style: const TextStyle(color: Colors.white70, fontSize: 18), style: const TextStyle(color: Colors.white70, fontSize: 18),
), ),
// امتیازِ کسب‌شده‌ی تورنومنت (برد ۱۰۰، شرکت ۲۵ — قانونِ ثابتِ سرور). // پاداشِ سکه‌ی برد (جایزه‌ی میز).
if (_tournament != null) ...[ if (won && widget.prize > 0) ...[
const SizedBox(height: 16), const SizedBox(height: 16),
Container( _rewardChip(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), Icons.monetization_on, '+${widget.prize} سکه'),
decoration: BoxDecoration(
color: AppColors.gold.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.gold),
),
child: Row(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.emoji_events, color: AppColors.gold, size: 20),
const SizedBox(width: 8),
Text(
'+${won ? 100 : 25} امتیاز تورنومنت «${_tournament!.title}»',
style: const TextStyle(
color: AppColors.gold,
fontSize: 14,
fontWeight: FontWeight.bold),
),
]),
),
], ],
const SizedBox(height: 28), // امتیازِ کسب‌شده‌ی تورنومنت (برد ۱۰۰، شرکت ۲۵).
if (_tournament != null) ...[
const SizedBox(height: 12),
_rewardChip(Icons.emoji_events,
'+${won ? 100 : 25} امتیاز تورنومنت «${_tournament!.title}»'),
],
const SizedBox(height: 30),
SizedBox( SizedBox(
width: 220, width: 220,
child: ElevatedButton( child: ElevatedButton(
@@ -671,8 +709,29 @@ class _GameScreenState extends State<GameScreen> {
), ),
], ],
), ),
),
),
],
); );
} }
Widget _rewardChip(IconData icon, String text) => Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: AppColors.gold.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.gold),
),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Icon(icon, color: AppColors.gold, size: 20),
const SizedBox(width: 8),
Text(text,
style: const TextStyle(
color: AppColors.gold,
fontSize: 14,
fontWeight: FontWeight.bold)),
]),
);
} }
/// آواتارِ در حالِ تغییر برای جایگاه‌های خالیِ پنل جستجو (حسِ «در حال یافتن»). /// آواتارِ در حالِ تغییر برای جایگاه‌های خالیِ پنل جستجو (حسِ «در حال یافتن»).
@@ -0,0 +1,119 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
/// بارشِ کاغذرنگی (کانفتی) سبک و بدونِ وابستگی — برای صفحه‌ی بردِ بازی.
class Confetti extends StatefulWidget {
final int count;
const Confetti({super.key, this.count = 60});
@override
State<Confetti> createState() => _ConfettiState();
}
class _ConfettiState extends State<Confetti> with SingleTickerProviderStateMixin {
late final AnimationController _c;
late final List<_Piece> _pieces;
static const _colors = [
Color(0xFFE9B949), // طلایی
Color(0xFF1E5FA8), // لاجوردی
Color(0xFFB81D2A), // شنگرف
Color(0xFF3FA34D), // سبز
Color(0xFFFFFFFF),
];
@override
void initState() {
super.initState();
final r = math.Random();
_pieces = List.generate(widget.count, (_) {
return _Piece(
x: r.nextDouble(),
delay: r.nextDouble() * 0.5,
speed: 0.7 + r.nextDouble() * 0.6,
drift: (r.nextDouble() - 0.5) * 0.4,
size: 6 + r.nextDouble() * 7,
color: _colors[r.nextInt(_colors.length)],
rot: r.nextDouble() * math.pi,
rotSpeed: (r.nextDouble() - 0.5) * 12,
round: r.nextBool(),
);
});
_c = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2600),
)..forward();
}
@override
void dispose() {
_c.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: AnimatedBuilder(
animation: _c,
builder: (_, __) => CustomPaint(
size: Size.infinite,
painter: _ConfettiPainter(_pieces, _c.value),
),
),
);
}
}
class _Piece {
final double x, delay, speed, drift, size, rot, rotSpeed;
final Color color;
final bool round;
_Piece({
required this.x,
required this.delay,
required this.speed,
required this.drift,
required this.size,
required this.color,
required this.rot,
required this.rotSpeed,
required this.round,
});
}
class _ConfettiPainter extends CustomPainter {
final List<_Piece> pieces;
final double t; // ۰..۱
_ConfettiPainter(this.pieces, this.t);
@override
void paint(Canvas canvas, Size size) {
for (final p in pieces) {
final local = ((t - p.delay) * p.speed).clamp(0.0, 1.0);
if (local <= 0) continue;
final y = -0.1 + local * 1.25;
final x = (p.x + p.drift * local) * size.width;
final py = y * size.height;
final opacity = (1.0 - local).clamp(0.0, 1.0);
final paint = Paint()..color = p.color.withValues(alpha: opacity);
canvas.save();
canvas.translate(x, py);
canvas.rotate(p.rot + p.rotSpeed * local);
if (p.round) {
canvas.drawCircle(Offset.zero, p.size / 2, paint);
} else {
canvas.drawRect(
Rect.fromCenter(center: Offset.zero, width: p.size, height: p.size * 0.6),
paint,
);
}
canvas.restore();
}
}
@override
bool shouldRepaint(covariant _ConfettiPainter old) => old.t != t;
}
@@ -2,6 +2,7 @@ import 'package:flame/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flame/events.dart'; import 'package:flame/events.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show HapticFeedback;
import '../../../../../core/network/card_images.dart'; import '../../../../../core/network/card_images.dart';
import '../../../../../core/theme/app_theme.dart'; import '../../../../../core/theme/app_theme.dart';
@@ -17,6 +18,7 @@ class CardComponent extends PositionComponent
VoidCallback? onPlay; // در صورت مجاز بودن، بازیِ این کارت VoidCallback? onPlay; // در صورت مجاز بودن، بازیِ این کارت
bool dimmed; // کارت غیرمجاز/غیرفعال bool dimmed; // کارت غیرمجاز/غیرفعال
Vector2? home; // موقعیت اصلی در دست (برای برگشت پس از کشیدنِ ناقص) Vector2? home; // موقعیت اصلی در دست (برای برگشت پس از کشیدنِ ناقص)
bool highlight = false; // کارتِ برنده‌ی دست ⇒ درخششِ طلایی پیش از جمع‌شدن
int restPriority = 0; // ترتیب لایه‌ی اصلی در دست (برای بازگردانی پس از کشیدن) int restPriority = 0; // ترتیب لایه‌ی اصلی در دست (برای بازگردانی پس از کشیدن)
Rect? dropZone; // ناحیه‌ی وسط میز؛ رهاکردن کارت در آن یعنی بازی Rect? dropZone; // ناحیه‌ی وسط میز؛ رهاکردن کارت در آن یعنی بازی
Sprite? _front; Sprite? _front;
@@ -76,6 +78,15 @@ class CardComponent extends PositionComponent
scale = Vector2.all(1); scale = Vector2.all(1);
} }
/// یک «پاپِ» کوتاه (بزرگ‌نماییِ رفت‌وبرگشتی) برای کارتِ برنده‌ی دست.
void pulse() {
children.whereType<ScaleEffect>().toList().forEach((e) => e.removeFromParent());
add(ScaleEffect.to(
Vector2.all(1.16),
EffectController(duration: 0.18, alternate: true, curve: Curves.easeOut),
));
}
@override @override
void onDragStart(DragStartEvent event) { void onDragStart(DragStartEvent event) {
super.onDragStart(event); super.onDragStart(event);
@@ -101,6 +112,7 @@ class CardComponent extends PositionComponent
// وگرنه برگشت به جای مرتبِ خود. // وگرنه برگشت به جای مرتبِ خود.
if (_inDropZone()) { if (_inDropZone()) {
resetScale(); resetScale();
HapticFeedback.selectionClick(); // بازخوردِ لمسیِ بازیِ کارت
onPlay?.call(); onPlay?.call();
} else { } else {
_scaleTo(1); _scaleTo(1);
@@ -184,6 +196,18 @@ class CardComponent extends PositionComponent
..color = AppColors.suitDark, ..color = AppColors.suitDark,
); );
// درخششِ طلاییِ کارتِ برنده‌ی دست (فقط یک کارت هم‌زمان ⇒ blur ارزان).
if (highlight) {
canvas.drawRRect(
rrect,
Paint()
..style = PaintingStyle.stroke
..strokeWidth = size.x * 0.07
..color = AppColors.gold
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6),
);
}
// هایلایت طلایی: هنگام کشیدن همیشه (انتخاب)، و پررنگ‌تر روی ناحیه‌ی انداختن. // هایلایت طلایی: هنگام کشیدن همیشه (انتخاب)، و پررنگ‌تر روی ناحیه‌ی انداختن.
if (_dragging) { if (_dragging) {
canvas.drawRRect( canvas.drawRRect(
@@ -5,6 +5,7 @@ import 'package:flame/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flame/game.dart'; import 'package:flame/game.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show HapticFeedback;
import '../../../../../core/service/app_sounds.dart'; import '../../../../../core/service/app_sounds.dart';
import '../../../../../core/theme/app_theme.dart'; import '../../../../../core/theme/app_theme.dart';
@@ -46,6 +47,7 @@ class HokmGame extends FlameGame {
double _shake = 0; double _shake = 0;
double _t = 0; double _t = 0;
List<String> _prevTrick = const []; List<String> _prevTrick = const [];
bool _highlightedTrick = false; // آیا کارتِ برنده‌ی دستِ کامل هایلایت شده (یک‌بار)
int _bestTrumpRank = 0; // بالاترین رتبه‌ی حکمِ بازی‌شده در دستِ جاری (برای افکتِ بریدن) int _bestTrumpRank = 0; // بالاترین رتبه‌ی حکمِ بازی‌شده در دستِ جاری (برای افکتِ بریدن)
int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن
String _prevHandSig = ''; // امضای دست؛ برای تشخیصِ بُرِ مجدد (تغییرِ ۵ کارت در فازِ حکم) String _prevHandSig = ''; // امضای دست؛ برای تشخیصِ بُرِ مجدد (تغییرِ ۵ کارت در فازِ حکم)
@@ -171,6 +173,7 @@ class HokmGame extends FlameGame {
_shake = 1.0; _shake = 1.0;
add(Lightning()); add(Lightning());
AppSounds.cut(); // بریدن با حکم (شمشیر) AppSounds.cut(); // بریدن با حکم (شمشیر)
HapticFeedback.heavyImpact(); // ضربه‌ی لمسیِ بُرِش با حکم
} else { } else {
AppSounds.placeCard(); // کارت روی میز نشست AppSounds.placeCard(); // کارت روی میز نشست
} }
@@ -475,22 +478,30 @@ class HokmGame extends FlameGame {
} }
final present = s.trick.map((t) => t.card).toSet(); final present = s.trick.map((t) => t.card).toSet();
// کارت‌های trick که دیگر نیستند (دست جمع شد) ⇒ به سمت برنده برو و حذف شو. // کارت‌های trick که دیگر نیستند (دست جمع شد) ⇒ برنده کارت‌ها را «جمع می‌کند»:
// به سمتِ جایگاهِ برنده (s.turn) پرواز می‌کنند، کوچک می‌شوند و محو (scoop).
final winnerOrigin = _seatOrigin(_rel(s.turn));
var i = 0;
for (final code in _trick.keys.toList()) { for (final code in _trick.keys.toList()) {
if (present.contains(code)) continue; if (present.contains(code)) continue;
final c = _trick.remove(code)!; final c = _trick.remove(code)!;
c.children.whereType<MoveToEffect>().toList().forEach( c.highlight = false;
(e) => e.removeFromParent(), c.priority = 30; // رویِ همه هنگام جمع‌شدن
); c.children.whereType<Effect>().toList().forEach((e) => e.removeFromParent());
c.add( final delay = i * 0.05; // ترتیبِ کوتاه برای حسِ «جارو کردن»
SequenceEffect([ c.add(ScaleEffect.to(
Vector2.all(0.5),
EffectController(duration: 0.34, startDelay: delay, curve: Curves.easeIn),
));
c.add(SequenceEffect([
MoveToEffect( MoveToEffect(
_seatOrigin(_rel(s.turn)), winnerOrigin,
EffectController(duration: 0.25, curve: Curves.easeIn), EffectController(
duration: 0.36, startDelay: delay, curve: Curves.easeInBack),
), ),
RemoveEffect(), RemoveEffect(),
]), ]));
); i++;
} }
for (final tc in s.trick) { for (final tc in s.trick) {
@@ -521,6 +532,45 @@ class HokmGame extends FlameGame {
c.priority = 5; c.priority = 5;
_moveTo(c, slot); _moveTo(c, slot);
} }
// دستِ کامل (۴ کارت) ⇒ کارتِ برنده را طلایی هایلایت کن و یک‌بار «پاپ» بزن،
// تا کاربر پیش از جمع‌شدنِ کارت‌ها بفهمد چه کسی برد.
if (s.trickDone && s.trick.length == 4) {
final ws = _winnerSeat(s.trick, s.trump, s.leadSuit);
for (final tc in s.trick) {
final comp = _trick[tc.card];
if (comp == null) continue;
final win = tc.seat == ws;
comp.highlight = win;
if (win && !_highlightedTrick) comp.pulse();
}
_highlightedTrick = true;
} else if (!s.trickDone) {
_highlightedTrick = false;
for (final c in _trick.values) {
c.highlight = false;
}
}
}
// برنده‌ی دستِ جاری را از روی کارت‌ها + حکم + خالِ زمینه حساب می‌کند (سمتِ کلاینت).
int _winnerSeat(List<TrickCard> trick, String? trump, String? lead) {
var best = trick.first;
for (final tc in trick.skip(1)) {
if (_beats(tc.card, best.card, trump, lead)) best = tc;
}
return best.seat;
}
bool _beats(String a, String b, String? trump, String? lead) {
final aS = suitName(a), bS = suitName(b);
final aT = aS == trump, bT = bS == trump;
if (aT && !bT) return true;
if (!aT && bT) return false;
if (aT && bT) return rankValue(a) > rankValue(b);
if (aS != lead) return false; // فقط کارتِ هم‌خالِ زمینه می‌تواند ببرد
if (bS != lead) return true;
return rankValue(a) > rankValue(b);
} }
// مبدأِ نشستنِ هر جایگاه (برای پرتاب/جمعِ کارت‌ها). // مبدأِ نشستنِ هر جایگاه (برای پرتاب/جمعِ کارت‌ها).
@@ -99,6 +99,21 @@ class Felt extends PositionComponent with HasGameReference {
); );
} }
// نورِ نرمِ مرکزی (اسپاتلایت) برای برجسته‌شدنِ کارت‌های وسطِ میز.
canvas.save();
canvas.clipRRect(table);
canvas.drawRect(
rect,
Paint()
..shader = RadialGradient(
center: Alignment.center,
radius: 0.62,
colors: const [Color(0x1FFFFFFF), Color(0x00FFFFFF)],
stops: const [0.0, 1.0],
).createShader(rect),
);
canvas.restore();
// خطِ طلاییِ نازکِ داخلی (قابِ دولایه). // خطِ طلاییِ نازکِ داخلی (قابِ دولایه).
canvas.drawRRect( canvas.drawRRect(
RRect.fromRectAndRadius(rect.deflate(w * 0.018), Radius.circular(h * 0.04)), RRect.fromRectAndRadius(rect.deflate(w * 0.018), Radius.circular(h * 0.04)),
@@ -87,7 +87,7 @@ class _LobbyScreenState extends State<LobbyScreen> {
icon: Icons.group_add, icon: Icons.group_add,
label: 'دورهمی', label: 'دورهمی',
sub: 'بازی با دوستان', sub: 'بازی با دوستان',
accent: AppColors.lapis, accent: AppColors.accent,
onTap: () async { onTap: () async {
await context.push('/private'); await context.push('/private');
if (context.mounted) _reload(); if (context.mounted) _reload();
@@ -122,15 +122,17 @@ class _LobbyScreenState extends State<LobbyScreen> {
const SizedBox(height: 14), const SizedBox(height: 14),
_DailyCard( _DailyCard(
wallet: wallet, wallet: wallet,
onTap: () => StreakDialog.show( onTap:
() => StreakDialog.show(
context, context,
streak: wallet?.dailyStreak ?? 0, streak: wallet?.dailyStreak ?? 0,
best: wallet?.bestStreak ?? 0, best: wallet?.bestStreak ?? 0,
ready: wallet?.dailyReady ?? true, ready: wallet?.dailyReady ?? true,
nextReward: wallet?.nextDaily ?? 200, nextReward: wallet?.nextDaily ?? 200,
onClaim: () => context onClaim:
.read<WalletBloc>() () => context.read<WalletBloc>().add(
.add(ClaimDailyEvent()), ClaimDailyEvent(),
),
), ),
), ),
], ],
@@ -142,8 +144,11 @@ class _LobbyScreenState extends State<LobbyScreen> {
context.read<AuthBloc>().add(LogoutEvent()); context.read<AuthBloc>().add(LogoutEvent());
context.go('/login'); context.go('/login');
}, },
icon: const Icon(Icons.logout, icon: const Icon(
color: Colors.white38, size: 18), Icons.logout,
color: Colors.white38,
size: 18,
),
label: const Text( label: const Text(
'خروج از حساب', 'خروج از حساب',
style: TextStyle(color: Colors.white38, fontSize: 13), style: TextStyle(color: Colors.white38, fontSize: 13),
@@ -175,8 +180,10 @@ class _LobbyHeader extends StatelessWidget {
SizedBox(height: 4), SizedBox(height: 4),
GlowText('حاکم ایرانی', size: 36), GlowText('حاکم ایرانی', size: 36),
SizedBox(height: 2), SizedBox(height: 2),
Text('بازیِ آنلاینِ حکم', Text(
style: TextStyle(color: Colors.white54, fontSize: 13)), 'بازیِ آنلاینِ حکم',
style: TextStyle(color: Colors.white54, fontSize: 13),
),
], ],
); );
} }
@@ -202,9 +209,14 @@ class _PrimaryPlay extends StatelessWidget {
border: Border.all(color: AppColors.gold, width: 2), border: Border.all(color: AppColors.gold, width: 2),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppColors.gold.withValues(alpha: 0.28), blurRadius: 16), color: AppColors.gold.withValues(alpha: 0.28),
blurRadius: 16,
),
const BoxShadow( const BoxShadow(
color: Colors.black54, blurRadius: 10, offset: Offset(0, 5)), color: Colors.black54,
blurRadius: 10,
offset: Offset(0, 5),
),
], ],
), ),
child: Row( child: Row(
@@ -213,30 +225,42 @@ class _PrimaryPlay extends StatelessWidget {
width: 54, width: 54,
height: 54, height: 54,
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: gradient: LinearGradient(
LinearGradient(colors: [AppColors.gold, AppColors.goldDark]), colors: [AppColors.gold, AppColors.goldDark],
),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: child: const Icon(
const Icon(Icons.play_arrow_rounded, color: AppColors.lapisInk, size: 36), Icons.play_arrow_rounded,
color: AppColors.lapisInk,
size: 36,
),
), ),
const SizedBox(width: 14), const SizedBox(width: 14),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: const [ children: const [
Text('بازی سریع', Text(
'بازی سریع',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold,
),
),
SizedBox(height: 2), SizedBox(height: 2),
Text('ورود به میزِ عمومی', Text(
style: TextStyle(color: Colors.white70, fontSize: 13)), 'ورود به میزِ عمومی',
style: TextStyle(color: Colors.white70, fontSize: 13),
),
], ],
), ),
const Spacer(), const Spacer(),
const Icon(Icons.arrow_back_ios_new, const Icon(
color: AppColors.gold, size: 18), Icons.arrow_forward_ios,
color: AppColors.gold,
size: 18,
),
], ],
), ),
), ),
@@ -295,15 +319,19 @@ class _ActionTile extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(label, Text(
label,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2), const SizedBox(height: 2),
Text(sub, Text(
style: sub,
const TextStyle(color: Colors.white54, fontSize: 11)), style: const TextStyle(color: Colors.white54, fontSize: 11),
),
], ],
), ),
], ],
@@ -328,7 +356,8 @@ class _DailyCard extends StatelessWidget {
final ready = w?.dailyReady ?? true; final ready = w?.dailyReady ?? true;
final next = w?.nextDaily ?? 200; final next = w?.nextDaily ?? 200;
final colors = ready final colors =
ready
? const [AppColors.success, AppColors.successDark] ? const [AppColors.success, AppColors.successDark]
: const [AppColors.lapisHi, AppColors.lapisLo]; : const [AppColors.lapisHi, AppColors.lapisLo];
@@ -353,17 +382,23 @@ class _DailyCard extends StatelessWidget {
Stack( Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
Icon(Icons.local_fire_department, Icon(
color: streak > 0 ? const Color(0xFFFF7A1A) : Colors.white54, Icons.local_fire_department,
size: 40), color:
streak > 0 ? const Color(0xFFFF7A1A) : Colors.white54,
size: 40,
),
if (streak > 0) if (streak > 0)
Padding( Padding(
padding: const EdgeInsets.only(top: 6), padding: const EdgeInsets.only(top: 6),
child: Text('$streak', child: Text(
'$streak',
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold,
),
),
), ),
], ],
), ),
@@ -371,14 +406,22 @@ class _DailyCard extends StatelessWidget {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('هدیه‌ی روزانه', const Text(
'هدیه‌ی روزانه',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold,
),
),
Text( Text(
streak > 0 ? '$streak روز پیاپی 🔥' : 'زنجیره‌ات رو شروع کن!', streak > 0
style: const TextStyle(color: Colors.white70, fontSize: 12), ? '$streak روز پیاپی 🔥'
: 'زنجیره‌ات رو شروع کن!',
style: const TextStyle(
color: Colors.white70,
fontSize: 12,
),
), ),
], ],
), ),
@@ -387,26 +430,42 @@ class _DailyCard extends StatelessWidget {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Row(mainAxisSize: MainAxisSize.min, children: [ Row(
const Icon(Icons.monetization_on, mainAxisSize: MainAxisSize.min,
color: AppColors.gold, size: 16), children: [
const Icon(
Icons.monetization_on,
color: AppColors.gold,
size: 16,
),
const SizedBox(width: 3), const SizedBox(width: 3),
Text('+$next', Text(
'+$next',
style: const TextStyle( style: const TextStyle(
color: AppColors.gold, color: AppColors.gold,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold,
]), ),
const Text('دریافت', ),
style: TextStyle(color: Colors.white70, fontSize: 11)), ],
),
const Text(
'دریافت',
style: TextStyle(color: Colors.white70, fontSize: 11),
),
], ],
) )
else else
const Row(mainAxisSize: MainAxisSize.min, children: [ const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check_circle, color: AppColors.gold, size: 18), Icon(Icons.check_circle, color: AppColors.gold, size: 18),
SizedBox(width: 4), SizedBox(width: 4),
Text('امروز گرفتی', Text(
style: TextStyle(color: Colors.white70, fontSize: 12)), 'امروز گرفتی',
]), style: TextStyle(color: Colors.white70, fontSize: 12),
),
],
),
], ],
), ),
), ),