From ffd2646eea150b2a7c747ba43d32099561708792 Mon Sep 17 00:00:00 2001 From: Amirmahdi Nourkazemi Date: Wed, 8 Jul 2026 00:17:46 +0330 Subject: [PATCH] feat: phase 1 of improve --- .../game/presentation/screen/game_screen.dart | 159 +++++++++----- .../game/presentation/widgets/confetti.dart | 119 ++++++++++ .../widgets/flame/card_component.dart | 24 +++ .../presentation/widgets/flame/hokm_game.dart | 76 +++++-- .../widgets/flame/table_pieces.dart | 15 ++ .../presentation/screen/lobby_screen.dart | 203 +++++++++++------- 6 files changed, 461 insertions(+), 135 deletions(-) create mode 100644 lib/feature/game/presentation/widgets/confetti.dart diff --git a/lib/feature/game/presentation/screen/game_screen.dart b/lib/feature/game/presentation/screen/game_screen.dart index 2001adf..dc52279 100644 --- a/lib/feature/game/presentation/screen/game_screen.dart +++ b/lib/feature/game/presentation/screen/game_screen.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show HapticFeedback; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.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_state.dart'; import '../../../chat/presentation/widgets/chat_panel.dart'; +import '../widgets/confetti.dart'; import '../widgets/flame/hokm_game.dart'; import '../widgets/table_hud.dart'; @@ -94,7 +96,10 @@ class _GameScreenState extends State { final won = state.gameOver!.winnerTeam == (state.state?.yourSeat ?? 0) % 2; - if (won) AppSounds.win(); + if (won) { + AppSounds.win(); + HapticFeedback.mediumImpact(); + } } if (state.notice != null) { ScaffoldMessenger.of(context).showSnackBar( @@ -619,60 +624,114 @@ class _GameScreenState extends State { final g = s.gameOver!; final mySeat = s.state?.yourSeat ?? 0; final won = g.winnerTeam == mySeat % 2; - return Container( - color: Colors.black87, - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - won ? 'بردید! 🎉' : 'باختید', - style: TextStyle( - color: won ? AppColors.gold : Colors.redAccent, - fontSize: 32, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 12), - Text( - 'نتیجه نهایی: ${g.scores.isNotEmpty ? g.scores[0] : 0} - ${g.scores.length > 1 ? g.scores[1] : 0}', - style: const TextStyle(color: Colors.white70, fontSize: 18), - ), - // امتیازِ کسب‌شده‌ی تورنومنت (برد ۱۰۰، شرکت ۲۵ — قانونِ ثابتِ سرور). - if (_tournament != null) ...[ - const SizedBox(height: 16), - 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: [ - 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), + return Stack( + children: [ + const Positioned.fill(child: ColoredBox(color: Colors.black87)), + if (won) const Positioned.fill(child: Confetti()), + Center( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // مدالِ متحرک (تاجِ طلایی برای برد، نشانِ ملایم برای باخت). + TweenAnimationBuilder( + 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: 28), - SizedBox( - width: 220, - child: ElevatedButton( - onPressed: () => _exitToLobby(context), - child: const Text('بازگشت به لابی'), + const SizedBox(height: 16), + Text( + won ? 'بردید! 🎉' : 'باختید', + style: TextStyle( + color: won ? AppColors.gold : Colors.redAccent, + fontSize: 34, + fontWeight: FontWeight.bold, + shadows: const [Shadow(color: Colors.black, blurRadius: 8)], + ), + ), + const SizedBox(height: 10), + Text( + 'نتیجه نهایی: ${g.scores.isNotEmpty ? g.scores[0] : 0} - ${g.scores.length > 1 ? g.scores[1] : 0}', + style: const TextStyle(color: Colors.white70, fontSize: 18), + ), + // پاداشِ سکه‌ی برد (جایزه‌ی میز). + if (won && widget.prize > 0) ...[ + const SizedBox(height: 16), + _rewardChip( + Icons.monetization_on, '+${widget.prize} سکه'), + ], + // امتیازِ کسب‌شده‌ی تورنومنت (برد ۱۰۰، شرکت ۲۵). + if (_tournament != null) ...[ + const SizedBox(height: 12), + _rewardChip(Icons.emoji_events, + '+${won ? 100 : 25} امتیاز تورنومنت «${_tournament!.title}»'), + ], + const SizedBox(height: 30), + SizedBox( + width: 220, + child: ElevatedButton( + onPressed: () => _exitToLobby(context), + child: const Text('بازگشت به لابی'), + ), + ), + ], ), ), - ], - ), + ), + ], ); } + + 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)), + ]), + ); } /// آواتارِ در حالِ تغییر برای جایگاه‌های خالیِ پنل جستجو (حسِ «در حال یافتن»). diff --git a/lib/feature/game/presentation/widgets/confetti.dart b/lib/feature/game/presentation/widgets/confetti.dart new file mode 100644 index 0000000..85db6b6 --- /dev/null +++ b/lib/feature/game/presentation/widgets/confetti.dart @@ -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 createState() => _ConfettiState(); +} + +class _ConfettiState extends State 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; +} diff --git a/lib/feature/game/presentation/widgets/flame/card_component.dart b/lib/feature/game/presentation/widgets/flame/card_component.dart index 901bfb1..e296608 100644 --- a/lib/feature/game/presentation/widgets/flame/card_component.dart +++ b/lib/feature/game/presentation/widgets/flame/card_component.dart @@ -2,6 +2,7 @@ import 'package:flame/components.dart'; import 'package:flame/effects.dart'; import 'package:flame/events.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show HapticFeedback; import '../../../../../core/network/card_images.dart'; import '../../../../../core/theme/app_theme.dart'; @@ -17,6 +18,7 @@ class CardComponent extends PositionComponent VoidCallback? onPlay; // در صورت مجاز بودن، بازیِ این کارت bool dimmed; // کارت غیرمجاز/غیرفعال Vector2? home; // موقعیت اصلی در دست (برای برگشت پس از کشیدنِ ناقص) + bool highlight = false; // کارتِ برنده‌ی دست ⇒ درخششِ طلایی پیش از جمع‌شدن int restPriority = 0; // ترتیب لایه‌ی اصلی در دست (برای بازگردانی پس از کشیدن) Rect? dropZone; // ناحیه‌ی وسط میز؛ رهاکردن کارت در آن یعنی بازی Sprite? _front; @@ -76,6 +78,15 @@ class CardComponent extends PositionComponent scale = Vector2.all(1); } + /// یک «پاپِ» کوتاه (بزرگ‌نماییِ رفت‌وبرگشتی) برای کارتِ برنده‌ی دست. + void pulse() { + children.whereType().toList().forEach((e) => e.removeFromParent()); + add(ScaleEffect.to( + Vector2.all(1.16), + EffectController(duration: 0.18, alternate: true, curve: Curves.easeOut), + )); + } + @override void onDragStart(DragStartEvent event) { super.onDragStart(event); @@ -101,6 +112,7 @@ class CardComponent extends PositionComponent // وگرنه برگشت به جای مرتبِ خود. if (_inDropZone()) { resetScale(); + HapticFeedback.selectionClick(); // بازخوردِ لمسیِ بازیِ کارت onPlay?.call(); } else { _scaleTo(1); @@ -184,6 +196,18 @@ class CardComponent extends PositionComponent ..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) { canvas.drawRRect( diff --git a/lib/feature/game/presentation/widgets/flame/hokm_game.dart b/lib/feature/game/presentation/widgets/flame/hokm_game.dart index 38fff43..d2b9a51 100644 --- a/lib/feature/game/presentation/widgets/flame/hokm_game.dart +++ b/lib/feature/game/presentation/widgets/flame/hokm_game.dart @@ -5,6 +5,7 @@ import 'package:flame/components.dart'; import 'package:flame/effects.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show HapticFeedback; import '../../../../../core/service/app_sounds.dart'; import '../../../../../core/theme/app_theme.dart'; @@ -46,6 +47,7 @@ class HokmGame extends FlameGame { double _shake = 0; double _t = 0; List _prevTrick = const []; + bool _highlightedTrick = false; // آیا کارتِ برنده‌ی دستِ کامل هایلایت شده (یک‌بار) int _bestTrumpRank = 0; // بالاترین رتبه‌ی حکمِ بازی‌شده در دستِ جاری (برای افکتِ بریدن) int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن String _prevHandSig = ''; // امضای دست؛ برای تشخیصِ بُرِ مجدد (تغییرِ ۵ کارت در فازِ حکم) @@ -171,6 +173,7 @@ class HokmGame extends FlameGame { _shake = 1.0; add(Lightning()); AppSounds.cut(); // بریدن با حکم (شمشیر) + HapticFeedback.heavyImpact(); // ضربه‌ی لمسیِ بُرِش با حکم } else { AppSounds.placeCard(); // کارت روی میز نشست } @@ -475,22 +478,30 @@ class HokmGame extends FlameGame { } 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()) { if (present.contains(code)) continue; final c = _trick.remove(code)!; - c.children.whereType().toList().forEach( - (e) => e.removeFromParent(), - ); - c.add( - SequenceEffect([ - MoveToEffect( - _seatOrigin(_rel(s.turn)), - EffectController(duration: 0.25, curve: Curves.easeIn), - ), - RemoveEffect(), - ]), - ); + c.highlight = false; + c.priority = 30; // رویِ همه هنگام جمع‌شدن + c.children.whereType().toList().forEach((e) => e.removeFromParent()); + final delay = i * 0.05; // ترتیبِ کوتاه برای حسِ «جارو کردن» + c.add(ScaleEffect.to( + Vector2.all(0.5), + EffectController(duration: 0.34, startDelay: delay, curve: Curves.easeIn), + )); + c.add(SequenceEffect([ + MoveToEffect( + winnerOrigin, + EffectController( + duration: 0.36, startDelay: delay, curve: Curves.easeInBack), + ), + RemoveEffect(), + ])); + i++; } for (final tc in s.trick) { @@ -521,6 +532,45 @@ class HokmGame extends FlameGame { c.priority = 5; _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 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); } // مبدأِ نشستنِ هر جایگاه (برای پرتاب/جمعِ کارت‌ها). diff --git a/lib/feature/game/presentation/widgets/flame/table_pieces.dart b/lib/feature/game/presentation/widgets/flame/table_pieces.dart index 2eda58a..ba7df4f 100644 --- a/lib/feature/game/presentation/widgets/flame/table_pieces.dart +++ b/lib/feature/game/presentation/widgets/flame/table_pieces.dart @@ -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( RRect.fromRectAndRadius(rect.deflate(w * 0.018), Radius.circular(h * 0.04)), diff --git a/lib/feature/wallet/presentation/screen/lobby_screen.dart b/lib/feature/wallet/presentation/screen/lobby_screen.dart index 7d5bf4f..91c3238 100644 --- a/lib/feature/wallet/presentation/screen/lobby_screen.dart +++ b/lib/feature/wallet/presentation/screen/lobby_screen.dart @@ -87,7 +87,7 @@ class _LobbyScreenState extends State { icon: Icons.group_add, label: 'دورهمی', sub: 'بازی با دوستان', - accent: AppColors.lapis, + accent: AppColors.accent, onTap: () async { await context.push('/private'); if (context.mounted) _reload(); @@ -122,16 +122,18 @@ class _LobbyScreenState extends State { 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() - .add(ClaimDailyEvent()), - ), + onTap: + () => StreakDialog.show( + context, + streak: wallet?.dailyStreak ?? 0, + best: wallet?.bestStreak ?? 0, + ready: wallet?.dailyReady ?? true, + nextReward: wallet?.nextDaily ?? 200, + onClaim: + () => context.read().add( + ClaimDailyEvent(), + ), + ), ), ], ), @@ -142,8 +144,11 @@ class _LobbyScreenState extends State { context.read().add(LogoutEvent()); context.go('/login'); }, - icon: const Icon(Icons.logout, - color: Colors.white38, size: 18), + icon: const Icon( + Icons.logout, + color: Colors.white38, + size: 18, + ), label: const Text( 'خروج از حساب', style: TextStyle(color: Colors.white38, fontSize: 13), @@ -175,8 +180,10 @@ class _LobbyHeader extends StatelessWidget { SizedBox(height: 4), GlowText('حاکم ایرانی', size: 36), SizedBox(height: 2), - Text('بازیِ آنلاینِ حکم', - style: TextStyle(color: Colors.white54, fontSize: 13)), + Text( + 'بازیِ آنلاینِ حکم', + style: TextStyle(color: Colors.white54, fontSize: 13), + ), ], ); } @@ -202,9 +209,14 @@ class _PrimaryPlay extends StatelessWidget { border: Border.all(color: AppColors.gold, width: 2), boxShadow: [ BoxShadow( - color: AppColors.gold.withValues(alpha: 0.28), blurRadius: 16), + color: AppColors.gold.withValues(alpha: 0.28), + blurRadius: 16, + ), const BoxShadow( - color: Colors.black54, blurRadius: 10, offset: Offset(0, 5)), + color: Colors.black54, + blurRadius: 10, + offset: Offset(0, 5), + ), ], ), child: Row( @@ -213,30 +225,42 @@ class _PrimaryPlay extends StatelessWidget { width: 54, height: 54, decoration: const BoxDecoration( - gradient: - LinearGradient(colors: [AppColors.gold, AppColors.goldDark]), + gradient: LinearGradient( + colors: [AppColors.gold, AppColors.goldDark], + ), shape: BoxShape.circle, ), - child: - const Icon(Icons.play_arrow_rounded, color: AppColors.lapisInk, size: 36), + 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)), + Text( + 'بازی سریع', + style: TextStyle( + color: Colors.white, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), SizedBox(height: 2), - Text('ورود به میزِ عمومی', - style: TextStyle(color: Colors.white70, fontSize: 13)), + Text( + 'ورود به میزِ عمومی', + style: TextStyle(color: Colors.white70, fontSize: 13), + ), ], ), const Spacer(), - const Icon(Icons.arrow_back_ios_new, - color: AppColors.gold, size: 18), + const Icon( + Icons.arrow_forward_ios, + color: AppColors.gold, + size: 18, + ), ], ), ), @@ -295,15 +319,19 @@ class _ActionTile extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Text(label, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold)), + 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)), + Text( + sub, + style: const TextStyle(color: Colors.white54, fontSize: 11), + ), ], ), ], @@ -328,9 +356,10 @@ class _DailyCard extends StatelessWidget { final ready = w?.dailyReady ?? true; final next = w?.nextDaily ?? 200; - final colors = ready - ? const [AppColors.success, AppColors.successDark] - : const [AppColors.lapisHi, AppColors.lapisLo]; + final colors = + ready + ? const [AppColors.success, AppColors.successDark] + : const [AppColors.lapisHi, AppColors.lapisLo]; return Opacity( opacity: ready ? 1 : 0.75, @@ -353,17 +382,23 @@ class _DailyCard extends StatelessWidget { Stack( alignment: Alignment.center, children: [ - Icon(Icons.local_fire_department, - color: streak > 0 ? const Color(0xFFFF7A1A) : Colors.white54, - size: 40), + 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)), + child: Text( + '$streak', + style: const TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold, + ), + ), ), ], ), @@ -371,14 +406,22 @@ class _DailyCard extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('هدیه‌ی روزانه', - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold)), + const Text( + 'هدیه‌ی روزانه', + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), Text( - streak > 0 ? '$streak روز پیاپی 🔥' : 'زنجیره‌ات رو شروع کن!', - style: const TextStyle(color: Colors.white70, fontSize: 12), + streak > 0 + ? '$streak روز پیاپی 🔥' + : 'زنجیره‌ات رو شروع کن!', + style: const TextStyle( + color: Colors.white70, + fontSize: 12, + ), ), ], ), @@ -387,26 +430,42 @@ class _DailyCard extends StatelessWidget { 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', + 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)), + 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)), - ]), + 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), + ), + ], + ), ], ), ),