diff --git a/lib/feature/game/presentation/screen/game_screen.dart b/lib/feature/game/presentation/screen/game_screen.dart index 54e95f7..851b30e 100644 --- a/lib/feature/game/presentation/screen/game_screen.dart +++ b/lib/feature/game/presentation/screen/game_screen.dart @@ -106,12 +106,13 @@ class _GameScreenState extends State { s: state.state!, connection: state.connection, onExit: () => _confirmLeave(context), - onChat: () => ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('گفتگو به‌زودی'), - duration: Duration(seconds: 1), - ), - ), + onChat: + () => ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('گفتگو به‌زودی'), + duration: Duration(seconds: 1), + ), + ), ), if (state.connection == WsStatus.disconnected) _connBanner(), if (_showSearch(state)) _searchPanel(state), @@ -122,10 +123,12 @@ class _GameScreenState extends State { valueListenable: _game.animating, builder: (context, animating, _) { if (animating) return const SizedBox.shrink(); - return Stack(children: [ - if (_showTrumpPicker(state)) _trumpPicker(context), - if (_isMyPlayTurn(state)) _turnBanner(), - ]); + return Stack( + children: [ + if (_showTrumpPicker(state)) _trumpPicker(context), + if (_isMyPlayTurn(state)) _turnBanner(), + ], + ); }, ), if (state.handResult != null && state.gameOver == null) @@ -149,7 +152,7 @@ class _GameScreenState extends State { // بنرِ «نوبت شماست» وقتی کاربر باید کارت بیندازد. Widget _turnBanner() => Align( - alignment: const Alignment(0, 0.80), + alignment: const Alignment(0, 0.3), child: IgnorePointer( child: Container( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 8), diff --git a/lib/feature/game/presentation/widgets/flame/hokm_game.dart b/lib/feature/game/presentation/widgets/flame/hokm_game.dart index 255efb6..697202a 100644 --- a/lib/feature/game/presentation/widgets/flame/hokm_game.dart +++ b/lib/feature/game/presentation/widgets/flame/hokm_game.dart @@ -63,7 +63,8 @@ class HokmGame extends FlameGame { // مدیریتِ ضربانِ قلب (فقط نوبتِ خودِ کاربر؛ مستقل از بازساختِ صحنه). double _turnElapsed = 0; - bool _beatPlayed = false; // یک‌بار در هر نوبت، هنگام رسیدن به آستانه + bool _beatPlayed = false; // اولین ضربان در پنجره‌ی پایانیِ نوبت پخش شد + double _hbAccum = 0; // انباشتِ زمان برای تکرارِ منظمِ ضربان تا پایانِ نوبت bool _wasMyTurn = false; HokmGame(this.cubit, {this.skin = 'simple'}); @@ -271,16 +272,27 @@ class HokmGame extends FlameGame { _wasMyTurn = true; _turnElapsed = 0; _beatPlayed = false; + _hbAccum = 0; } _turnElapsed += dt; final dur = (s.turnTimeoutMs > 0 ? s.turnTimeoutMs / 1000.0 : 30.0); final remaining = dur - _turnElapsed; - final warn = math.min(8.0, dur * 0.4); - // فقط یک‌بار، دقیقاً وقتی به آستانه‌ی پایانی رسیدیم. - if (!_beatPlayed && remaining > 0 && remaining <= warn) { - _beatPlayed = true; - AppSounds.heartBeat(); + final warn = math.min(7.0, dur * 0.4); + // در پنجره‌ی پایانیِ نوبت، ضربان را با فاصله‌ی منظم تکرار کن تا تا لحظه‌ی + // آخر شنیده شود (نه فقط یک ضربانِ زودهنگام که در سکوتِ پایانی گم می‌شود). + if (remaining > 0 && remaining <= warn) { + if (!_beatPlayed) { + _beatPlayed = true; + _hbAccum = 0; + AppSounds.heartBeat(); + } else { + _hbAccum += dt; + if (_hbAccum >= 0.95) { + _hbAccum = 0; + AppSounds.heartBeat(); + } + } } } @@ -524,32 +536,8 @@ class HokmGame extends FlameGame { final count = seat < s.handCounts.length ? s.handCounts[seat] : 0; _addBacks(_rel(seat), count); } - _addTricksWon(s); - // امتیاز/نام/نشان/تاج/شمارنده‌ی نوبت اکنون در اوورلیِ Flutter (TableHud) کشیده - // می‌شوند تا متن واضح‌تر و چیدمان نزدیک به تصویرِ مرجع باشد. - } - - // شمارنده ۱: دست‌های برده‌ی این هَند (tricks_won) — دسته‌کارتِ پشت‌رو + عدد. - // رسیدن به ۷ یعنی پایان هَند (سرور صفر می‌کند). - void _addTricksWon(GameState s) { - for (var team = 0; team < 2; team++) { - final won = team < s.tricksWon.length ? s.tricksWon[team] : 0; - if (won <= 0) continue; - final mine = team == s.yourSeat % 2; - final w = _cardW * 0.40, h = w * kCardRatio; - // تیم شما جلوی شما (پایین)، تیم حریف جلوی حریفِ سمت راست. - final base = - mine - ? Vector2(size.x * 0.28, size.y * 0.72) - : Vector2(size.x * 0.82, size.y * 0.24); - final step = Vector2(w * 0.40, 0); - final n = won.clamp(1, 7); - final start = base - step * ((n - 1) / 2); - for (var i = 0; i < n; i++) { - _addBack(start + step * i.toDouble(), Vector2(w, h), priority: 2); - } - _addLabel('$won', base - Vector2(0, h * 0.7), _cardW * 0.34, bold: true); - } + // امتیاز/نام/نشان/تاج/شمارنده‌ی نوبت و دست‌های برده (tricks_won) اکنون در + // اوورلیِ Flutter (TableHud) نمایش داده می‌شوند تا واضح‌تر و کم‌شلوغ‌تر باشند. } // پشت‌کارت‌های یک حریف به‌صورت بادبزنی. @@ -559,67 +547,47 @@ class HokmGame extends FlameGame { final w = _cardW * 0.7, h = _cardH * 0.7; final c = size / 2; Vector2 base, stepV; + double angle = 0; // کارت‌های کناری ۹۰° می‌چرخند تا «به‌سمتِ» بازیکنِ کنار باشند switch (rel) { case 2: base = Vector2(c.x, size.y * 0.12); stepV = Vector2(size.x * 0.45 / 13, 0); break; case 1: - base = Vector2(size.x * 0.07, c.y); - stepV = Vector2(0, size.y * 0.4 / 13); + // مرکزِ کارت روی لبه‌ی چپ ⇒ فقط نیمه‌ی داخلیِ کارت دیده می‌شود (شلوغیِ کمتر). + base = Vector2(0, c.y); + stepV = Vector2(0, size.y * 0.36 / 13); + angle = math.pi / 2; break; default: - base = Vector2(size.x * 0.93, c.y); - stepV = Vector2(0, size.y * 0.4 / 13); + base = Vector2(size.x, c.y); + stepV = Vector2(0, size.y * 0.36 / 13); + angle = -math.pi / 2; } final start = base - stepV * ((n - 1) / 2); for (var i = 0; i < n; i++) { - _addBack(start + stepV * i.toDouble(), Vector2(w, h), priority: 1); + _addBack(start + stepV * i.toDouble(), Vector2(w, h), + priority: 1, angle: angle); } } // ===== کمکی‌های ساختِ عنصر (ثبت در فهرستِ بازساخته‌شونده) ===== - void _addBack(Vector2 pos, Vector2 sz, {required int priority}) { + void _addBack(Vector2 pos, Vector2 sz, + {required int priority, double angle = 0}) { final c = CardComponent( code: '', faceUp: false, skin: skin, size: sz, position: pos, + angle: angle, priority: priority, ); _backs.add(c); add(c); } - void _addLabel( - String text, - Vector2 pos, - double fontSize, { - Color color = const Color(0xFFE9B949), - Anchor anchor = Anchor.center, - bool bold = false, - }) { - final t = TextComponent( - text: text, - anchor: anchor, - position: pos, - priority: 21, - textRenderer: TextPaint( - style: TextStyle( - fontFamily: 'Dana', - color: color, - fontSize: fontSize, - fontWeight: bold ? FontWeight.bold : FontWeight.normal, - shadows: const [Shadow(color: Colors.black, blurRadius: 4)], - ), - ), - ); - _info.add(t); - add(t); - } - (String, Color) _trumpGlyph(String suit) { switch (suit) { case 'hearts': diff --git a/lib/feature/game/presentation/widgets/table_hud.dart b/lib/feature/game/presentation/widgets/table_hud.dart index 12ee0d7..4f9314f 100644 --- a/lib/feature/game/presentation/widgets/table_hud.dart +++ b/lib/feature/game/presentation/widgets/table_hud.dart @@ -24,60 +24,67 @@ class TableHud extends StatelessWidget { // مکانِ آواتارِ هر جایگاهِ نسبی (۰=پایین/شما، ۱=چپ، ۲=بالا، ۳=راست). static const _spots = { 0: Alignment(0, 1.0), - 1: Alignment(-0.94, -0.08), + 1: Alignment(-1.0, -0.05), // چسبیده به لبه‌ی چپ 2: Alignment(0, -0.92), - 3: Alignment(0.94, -0.08), + 3: Alignment(1.0, -0.05), // چسبیده به لبه‌ی راست }; int _rel(int seat) => (seat - s.yourSeat + 4) % 4; @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, c) { - final w = c.maxWidth; - final avatarD = (w * 0.17).clamp(48.0, 84.0); - return Stack( - children: [ - for (final p in s.players) - Align( - alignment: _spots[_rel(p.seat)]!, - child: Padding( - padding: EdgeInsets.only( - bottom: _rel(p.seat) == 0 ? 2 : 0, - top: _rel(p.seat) == 2 ? 6 : 0, - ), - child: _PlayerSeat( - player: p, - diameter: avatarD, - isTurn: s.turn == p.seat, - isHakem: s.hakem == p.seat, - nameAbove: _rel(p.seat) == 2, - cardCount: - p.seat < s.handCounts.length ? s.handCounts[p.seat] : 0, - turnToken: '${s.turn}|${s.trick.length}|${s.phase}', - turnSeconds: s.turnTimeoutMs > 0 ? s.turnTimeoutMs / 1000 : 30, - showRing: s.phase == 'playing' || s.phase == 'choose_trump', + return LayoutBuilder( + builder: (context, c) { + final w = c.maxWidth; + final avatarD = (w * 0.17).clamp(48.0, 84.0); + return Stack( + children: [ + for (final p in s.players) + Align( + alignment: _spots[_rel(p.seat)]!, + child: Padding( + padding: EdgeInsets.only( + bottom: _rel(p.seat) == 0 ? 2 : 0, + top: _rel(p.seat) == 2 ? 6 : 0, + ), + child: _PlayerSeat( + player: p, + diameter: avatarD, + isTurn: s.turn == p.seat, + isHakem: s.hakem == p.seat, + nameAbove: _rel(p.seat) == 2, + cardCount: + p.seat < s.handCounts.length ? s.handCounts[p.seat] : 0, + teamTricks: + (p.seat % 2) < s.tricksWon.length + ? s.tricksWon[p.seat % 2] + : 0, + turnToken: '${s.turn}|${s.trick.length}|${s.phase}', + turnSeconds: + s.turnTimeoutMs > 0 ? s.turnTimeoutMs / 1000 : 30, + showRing: s.phase == 'playing' || s.phase == 'choose_trump', + ), ), ), + Positioned(top: 8, left: 8, child: SafeArea(child: _scorePanel())), + Positioned( + top: 8, + right: 8, + child: SafeArea(child: _topRight(context)), ), - Positioned(top: 8, left: 8, child: SafeArea(child: _scorePanel())), - Positioned( - top: 8, - right: 8, - child: SafeArea(child: _topRight(context)), - ), - Positioned( - bottom: 16, - right: 12, - child: _RoundBtn( - icon: Icons.chat_bubble, - color: const Color(0xFF1E5FA8), - onTap: onChat, + Positioned( + bottom: 16, + right: 12, + child: _RoundBtn( + icon: Icons.chat_bubble, + color: const Color(0xFF1E5FA8), + onTap: onChat, + ), ), - ), - ], - ); - }); + ], + ); + }, + ); } // پنلِ امتیاز (ما/حریف) + چیپِ حکم. امتیازِ تیمِ شما = scores[yourSeat%2]. @@ -111,16 +118,21 @@ class TableHud extends StatelessWidget { } Widget _scoreCol(String label, int v) => Column( - children: [ - Text(label, - style: const TextStyle(color: Color(0xFFE9B949), fontSize: 12)), - Text('$v', - style: const TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.bold)), - ], - ); + children: [ + Text( + label, + style: const TextStyle(color: Color(0xFFE9B949), fontSize: 12), + ), + Text( + '$v', + style: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ); Widget _hokmChip() { final (glyph, color) = _trumpGlyph(s.trump); @@ -130,11 +142,14 @@ class TableHud extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Text('حکم', - style: TextStyle( - color: Color(0xFFE9B949), - fontSize: 13, - fontWeight: FontWeight.bold)), + const Text( + 'حکم', + style: TextStyle( + color: Color(0xFFE9B949), + fontSize: 13, + fontWeight: FontWeight.bold, + ), + ), const SizedBox(width: 6), Text(glyph, style: TextStyle(color: color, fontSize: 18)), ], @@ -151,15 +166,20 @@ class TableHud extends StatelessWidget { decoration: _panelDeco(), child: Row( children: [ - Icon(ok ? Icons.wifi : Icons.wifi_off, - color: ok ? const Color(0xFF4CD964) : Colors.redAccent, - size: 16), + Icon( + ok ? Icons.wifi : Icons.wifi_off, + color: ok ? const Color(0xFF4CD964) : Colors.redAccent, + size: 16, + ), const SizedBox(width: 5), - Text(ok ? 'آنلاین' : 'قطع', - style: TextStyle( - color: ok ? const Color(0xFF4CD964) : Colors.redAccent, - fontSize: 12, - fontWeight: FontWeight.bold)), + Text( + ok ? 'آنلاین' : 'قطع', + style: TextStyle( + color: ok ? const Color(0xFF4CD964) : Colors.redAccent, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), ], ), ), @@ -175,15 +195,15 @@ class TableHud extends StatelessWidget { } static BoxDecoration _panelDeco() => BoxDecoration( - gradient: const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xFF2A2018), Color(0xFF14100B)], - ), - borderRadius: BorderRadius.circular(14), - border: Border.all(color: const Color(0xFFB8860B), width: 1.4), - boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 6)], - ); + gradient: const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xFF2A2018), Color(0xFF14100B)], + ), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: const Color(0xFFB8860B), width: 1.4), + boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 6)], + ); (String, Color) _trumpGlyph(String? suit) { switch (suit) { @@ -208,6 +228,8 @@ class _PlayerSeat extends StatelessWidget { final bool isHakem; final bool nameAbove; final int cardCount; + final int + teamTricks; // دست‌های برده‌ی تیمِ این بازیکن در این هَند (tricks_won) final String turnToken; final double turnSeconds; final bool showRing; @@ -218,6 +240,7 @@ class _PlayerSeat extends StatelessWidget { required this.isHakem, required this.nameAbove, required this.cardCount, + required this.teamTricks, required this.turnToken, required this.turnSeconds, required this.showRing, @@ -228,12 +251,13 @@ class _PlayerSeat extends StatelessWidget { final ringW = diameter * 0.08; final name = Container( constraints: BoxConstraints(maxWidth: diameter * 1.7), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 1), decoration: BoxDecoration( color: const Color(0xCC14100B), borderRadius: BorderRadius.circular(10), border: Border.all( - color: isTurn ? const Color(0xFFE9B949) : const Color(0x55B8860B)), + color: isTurn ? const Color(0xFFE9B949) : const Color(0x55B8860B), + ), ), child: Text( player.bot ? '${player.name} (ربات)' : player.name, @@ -266,16 +290,22 @@ class _PlayerSeat extends StatelessWidget { end: Alignment.bottomCenter, colors: [Color(0xFFF3D27A), Color(0xFFB8860B)], ), - boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 6)], + boxShadow: const [ + BoxShadow(color: Colors.black54, blurRadius: 6), + ], ), padding: EdgeInsets.all(ringW), child: ClipOval( child: Container( color: const Color(0xFF1A2740), - child: player.bot - ? Icon(Icons.smart_toy, - color: Colors.white70, size: diameter * 0.5) - : RandomAvatar(player.name.isEmpty ? '?' : player.name), + child: + player.bot + ? Icon( + Icons.smart_toy, + color: Colors.white70, + size: diameter * 0.5, + ) + : RandomAvatar(player.name.isEmpty ? '?' : player.name), ), ), ), @@ -291,45 +321,32 @@ class _PlayerSeat extends StatelessWidget { // تاجِ حاکم بالای آواتار. if (isHakem) Positioned( - top: -diameter * 0.32, - child: Icon(Icons.workspace_premium, - color: const Color(0xFFFFC107), size: diameter * 0.42), + top: -diameter * 0.40, + child: Text('👑', style: TextStyle(fontSize: diameter * 0.4)), ), - // حبابِ تعدادِ کارت (پایین-راست). - if (cardCount > 0) + // دست‌های برده‌ی تیمِ این بازیکن در این هَند (بالا-چپ، با نشانِ تک‌خال). + if (teamTricks > 0) Positioned( - right: -diameter * 0.04, - bottom: -diameter * 0.04, - child: Container( - padding: EdgeInsets.all(diameter * 0.06), - constraints: BoxConstraints( - minWidth: diameter * 0.34, minHeight: diameter * 0.34), - decoration: BoxDecoration( - color: const Color(0xFF0E2A12), - shape: BoxShape.circle, - border: Border.all(color: const Color(0xFFE9B949), width: 1.4), - ), - alignment: Alignment.center, - child: Text('$cardCount', - style: TextStyle( - color: Colors.white, - fontSize: diameter * 0.18, - fontWeight: FontWeight.bold)), - ), + left: -diameter * 0.06, + top: -diameter * 0.06, + child: _TricksBadge(count: teamTricks, diameter: diameter), ), + + // حبابِ تعدادِ کارت (پایین-راست). ], ), ); // نشانِ رتبه‌ی فشرده (تصویر) کنارِ آواتار. - final badge = (!player.bot && player.rankTier.isNotEmpty) - ? Image.asset( - 'assets/images/badge/${player.rankTier}.png', - width: diameter * 0.34, - height: diameter * 0.34, - errorBuilder: (_, __, ___) => const SizedBox.shrink(), - ) - : const SizedBox.shrink(); + final badge = + (!player.bot && player.rankTier.isNotEmpty) + ? Image.asset( + 'assets/images/badge/${player.rankTier}.png', + width: diameter * 0.34, + height: diameter * 0.34, + errorBuilder: (_, __, ___) => const SizedBox.shrink(), + ) + : const SizedBox.shrink(); final nameRow = Row( mainAxisSize: MainAxisSize.min, @@ -339,18 +356,23 @@ class _PlayerSeat extends StatelessWidget { Flexible(child: name), if (!player.bot && player.coins > 0) ...[ const SizedBox(width: 4), - Text('${player.coins}', - style: TextStyle( - color: const Color(0xFFE9B949), fontSize: diameter * 0.16)), + Text( + '${player.coins}', + style: TextStyle( + color: const Color(0xFFE9B949), + fontSize: diameter * 0.16, + ), + ), ], ], ); return Column( mainAxisSize: MainAxisSize.min, - children: nameAbove - ? [nameRow, SizedBox(height: diameter * 0.06), avatar] - : [avatar, SizedBox(height: diameter * 0.06), nameRow], + children: + nameAbove + ? [nameRow, SizedBox(height: diameter * 0.06), avatar] + : [avatar, SizedBox(height: diameter * 0.06), nameRow], ); } } @@ -361,8 +383,11 @@ class _TurnRing extends StatefulWidget { final String token; final double seconds; final double stroke; - const _TurnRing( - {required this.token, required this.seconds, required this.stroke}); + const _TurnRing({ + required this.token, + required this.seconds, + required this.stroke, + }); @override State<_TurnRing> createState() => _TurnRingState(); @@ -376,8 +401,9 @@ class _TurnRingState extends State<_TurnRing> void initState() { super.initState(); _c = AnimationController( - vsync: this, duration: Duration(milliseconds: (widget.seconds * 1000).round())) - ..forward(); + vsync: this, + duration: Duration(milliseconds: (widget.seconds * 1000).round()), + )..forward(); } @override @@ -423,7 +449,8 @@ class _RingPainter extends CustomPainter { final radius = (size.shortestSide - stroke) / 2; const start = -1.5708; // بالا final sweep = 6.28318 * left; - final color = left < 0.3 ? const Color(0xFFE53935) : const Color(0xFF4CD964); + final color = + left < 0.3 ? const Color(0xFFE53935) : const Color(0xFF4CD964); canvas.drawArc( Rect.fromCircle(center: center, radius: radius), start, @@ -442,16 +469,92 @@ class _RingPainter extends CustomPainter { old.left != left || old.stroke != stroke; } +/// نشانِ دست‌های برده‌ی تیم؛ هنگامِ نمایش و هر بار که عدد زیاد می‌شود یک «پاپ» +/// (بزرگ‌نماییِ کوتاه) می‌زند تا کاربر بفهمد دست‌های برده اینجا نشان داده می‌شوند. +class _TricksBadge extends StatefulWidget { + final int count; + final double diameter; + const _TricksBadge({required this.count, required this.diameter}); + + @override + State<_TricksBadge> createState() => _TricksBadgeState(); +} + +class _TricksBadgeState extends State<_TricksBadge> + with SingleTickerProviderStateMixin { + late final AnimationController _c; + late final Animation _scale; + + @override + void initState() { + super.initState(); + _c = AnimationController( + vsync: this, duration: const Duration(milliseconds: 420)); + _scale = TweenSequence([ + TweenSequenceItem(tween: Tween(begin: 0.4, end: 1.35), weight: 50), + TweenSequenceItem(tween: Tween(begin: 1.35, end: 1.0), weight: 50), + ]).animate(CurvedAnimation(parent: _c, curve: Curves.easeOut)); + _c.forward(); // پاپِ اولیه هنگامِ ظاهرشدن + } + + @override + void didUpdateWidget(covariant _TricksBadge old) { + super.didUpdateWidget(old); + if (old.count != widget.count) { + _c + ..reset() + ..forward(); // پاپ هر بار که دستِ برده اضافه می‌شود + } + } + + @override + void dispose() { + _c.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final d = widget.diameter; + return ScaleTransition( + scale: _scale, + child: Container( + padding: + EdgeInsets.symmetric(horizontal: d * 0.08, vertical: d * 0.03), + decoration: BoxDecoration( + color: const Color(0xFF7B0E14), + borderRadius: BorderRadius.circular(d), + border: Border.all(color: const Color(0xFFE9B949), width: 1.4), + boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 4)], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.layers, color: Colors.white, size: d * 0.18), + SizedBox(width: d * 0.03), + Text('${widget.count}', + style: TextStyle( + color: Colors.white, + fontSize: d * 0.2, + fontWeight: FontWeight.bold)), + ], + ), + ), + ); + } +} + class _RoundBtn extends StatelessWidget { final IconData icon; final Color color; final VoidCallback onTap; final double size; - const _RoundBtn( - {required this.icon, - required this.color, - required this.onTap, - this.size = 46}); + const _RoundBtn({ + required this.icon, + required this.color, + required this.onTap, + this.size = 46, + }); @override Widget build(BuildContext context) {