fix: some buggs
This commit is contained in:
Binary file not shown.
@@ -14,8 +14,17 @@ class AppSounds {
|
|||||||
static const _cut = 'cut.mp3'; // بریدن با حکم (شمشیر)
|
static const _cut = 'cut.mp3'; // بریدن با حکم (شمشیر)
|
||||||
static const _win = 'win.mp3'; // برد بازی
|
static const _win = 'win.mp3'; // برد بازی
|
||||||
static const _button = 'button.mp3'; // فشردنِ دکمهها
|
static const _button = 'button.mp3'; // فشردنِ دکمهها
|
||||||
|
static const _selectHokm = 'select_hokm.mp3'; // انتخابِ حکم
|
||||||
|
|
||||||
static const _all = [_shuffle, _placeCard, _takeCard, _cut, _win, _button];
|
static const _all = [
|
||||||
|
_shuffle,
|
||||||
|
_placeCard,
|
||||||
|
_takeCard,
|
||||||
|
_cut,
|
||||||
|
_win,
|
||||||
|
_button,
|
||||||
|
_selectHokm,
|
||||||
|
];
|
||||||
|
|
||||||
static final Map<String, AudioPool> _pools = {};
|
static final Map<String, AudioPool> _pools = {};
|
||||||
static bool muted = false;
|
static bool muted = false;
|
||||||
@@ -47,5 +56,6 @@ class AppSounds {
|
|||||||
static void takeCard() => _play(_takeCard, 0.7);
|
static void takeCard() => _play(_takeCard, 0.7);
|
||||||
static void cut() => _play(_cut, 0.9);
|
static void cut() => _play(_cut, 0.9);
|
||||||
static void win() => _play(_win, 0.9);
|
static void win() => _play(_win, 0.9);
|
||||||
|
static void selectHokm() => _play(_selectHokm, 0.9);
|
||||||
static void button() => _play(_button, 0.5, cutoffMs: 130); // کلیکِ کوتاه
|
static void button() => _play(_button, 0.5, cutoffMs: 130); // کلیکِ کوتاه
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class GameState {
|
|||||||
final List<int> tricksWon;
|
final List<int> tricksWon;
|
||||||
final List<int> scores;
|
final List<int> scores;
|
||||||
final int targetScore;
|
final int targetScore;
|
||||||
|
final int turnTimeoutMs; // مهلتِ نوبتِ انسان (برای شمارندهی نوبت)
|
||||||
final List<GamePlayer> players;
|
final List<GamePlayer> players;
|
||||||
|
|
||||||
GameState({
|
GameState({
|
||||||
@@ -54,6 +55,7 @@ class GameState {
|
|||||||
required this.tricksWon,
|
required this.tricksWon,
|
||||||
required this.scores,
|
required this.scores,
|
||||||
required this.targetScore,
|
required this.targetScore,
|
||||||
|
required this.turnTimeoutMs,
|
||||||
required this.players,
|
required this.players,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -78,6 +80,7 @@ class GameState {
|
|||||||
tricksWon: ints(j['tricks_won']),
|
tricksWon: ints(j['tricks_won']),
|
||||||
scores: ints(j['scores']),
|
scores: ints(j['scores']),
|
||||||
targetScore: (j['target_score'] ?? 7) as int,
|
targetScore: (j['target_score'] ?? 7) as int,
|
||||||
|
turnTimeoutMs: (j['turn_timeout_ms'] ?? 0) as int,
|
||||||
players: ((j['players'] as List?) ?? [])
|
players: ((j['players'] as List?) ?? [])
|
||||||
.map((e) => GamePlayer.fromJson(Map<String, dynamic>.from(e as Map)))
|
.map((e) => GamePlayer.fromJson(Map<String, dynamic>.from(e as Map)))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|||||||
@@ -68,13 +68,18 @@ class _GameScreenState extends State<GameScreen> {
|
|||||||
duration: const Duration(seconds: 2)),
|
duration: const Duration(seconds: 2)),
|
||||||
);
|
);
|
||||||
context.read<GameBloc>().clearNotice();
|
context.read<GameBloc>().clearNotice();
|
||||||
|
// خطا پیش از شروعِ بازی (مثلاً سکهی ناکافی) ⇒ کاربر در دیالوگِ
|
||||||
|
// «جستجوی حریف» گیر نکند؛ به لابی برگردد.
|
||||||
|
if (state.state == null && state.gameOver == null) {
|
||||||
|
_exitToLobby(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.state != null && _introTimer == null) {
|
if (state.state != null && _introTimer == null) {
|
||||||
// کمی «حریفان پیدا شد» نشان بده، بعد اوورلی را کنار بزن و همان
|
// کمی «حریفان پیدا شد» نشان بده، بعد اوورلی را کنار بزن و همان
|
||||||
// لحظه انیمیشنِ بُر زدن (با صدا) را اجرا کن.
|
// لحظه انیمیشنِ بُر زدن (با صدا) را اجرا کن.
|
||||||
_introTimer = Timer(const Duration(milliseconds: 700), () {
|
_introTimer = Timer(const Duration(milliseconds: 1000), () {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _introHidden = true);
|
setState(() => _introHidden = true);
|
||||||
_game.endIntro();
|
_game.endIntro();
|
||||||
@@ -90,6 +95,7 @@ class _GameScreenState extends State<GameScreen> {
|
|||||||
_trumpPicker(context),
|
_trumpPicker(context),
|
||||||
if (state.handResult != null && state.gameOver == null)
|
if (state.handResult != null && state.gameOver == null)
|
||||||
_handResult(state),
|
_handResult(state),
|
||||||
|
if (!_showSearch(state) && _isMyPlayTurn(state)) _turnBanner(),
|
||||||
if (state.gameOver != null) _gameOver(context, state),
|
if (state.gameOver != null) _gameOver(context, state),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -99,6 +105,34 @@ class _GameScreenState extends State<GameScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isMyPlayTurn(GameUiState s) =>
|
||||||
|
s.state != null &&
|
||||||
|
s.gameOver == null &&
|
||||||
|
s.state!.phase == 'playing' &&
|
||||||
|
s.state!.isMyTurn &&
|
||||||
|
!s.state!.trickDone;
|
||||||
|
|
||||||
|
// بنرِ «نوبت شماست» وقتی کاربر باید کارت بیندازد.
|
||||||
|
Widget _turnBanner() => Align(
|
||||||
|
alignment: const Alignment(0, 0.46),
|
||||||
|
child: IgnorePointer(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: const LinearGradient(
|
||||||
|
colors: [Color(0xFFE9B949), Color(0xFFB8860B)]),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 8)],
|
||||||
|
),
|
||||||
|
child: const Text('نوبت شماست',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFF3A0A12),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
bool _showTrumpPicker(GameUiState s) =>
|
bool _showTrumpPicker(GameUiState s) =>
|
||||||
s.state != null &&
|
s.state != null &&
|
||||||
s.state!.phase == 'choose_trump' &&
|
s.state!.phase == 'choose_trump' &&
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame/effects.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
const _gold = Color(0xFFE9B949);
|
||||||
|
const _goldDark = Color(0xFF8A5A00);
|
||||||
|
|
||||||
|
/// ستارهی طلایی روی بازیکنِ نوبتدار (مطابقِ تصویرِ مرجع).
|
||||||
|
class StarBadge extends PositionComponent {
|
||||||
|
final double r;
|
||||||
|
StarBadge(this.r, Vector2 pos)
|
||||||
|
: super(position: pos, size: Vector2.all(r * 2), anchor: Anchor.center);
|
||||||
|
|
||||||
|
static final _fill = Paint()..color = _gold;
|
||||||
|
static final _stroke = Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 1.5
|
||||||
|
..color = _goldDark;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void render(Canvas canvas) {
|
||||||
|
final path = _starPath(Offset(r, r), r, r * 0.45, 5);
|
||||||
|
canvas.drawPath(path, _fill);
|
||||||
|
canvas.drawPath(path, _stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// تاجِ طلایی روی حاکم.
|
||||||
|
class CrownBadge extends PositionComponent {
|
||||||
|
final double w;
|
||||||
|
CrownBadge(this.w, Vector2 pos)
|
||||||
|
: super(
|
||||||
|
position: pos,
|
||||||
|
size: Vector2(w, w * 0.8),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
static final _fill = Paint()..color = _gold;
|
||||||
|
static final _stroke = Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 1.5
|
||||||
|
..color = _goldDark;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void render(Canvas canvas) {
|
||||||
|
final h = size.y;
|
||||||
|
final p = Path()
|
||||||
|
..moveTo(0, h) // پایینچپ
|
||||||
|
..lineTo(0, h * 0.35) // نوکِ چپ
|
||||||
|
..lineTo(w * 0.25, h * 0.62) // فرورفتگی
|
||||||
|
..lineTo(w * 0.5, h * 0.18) // نوکِ میانی (بلندترین)
|
||||||
|
..lineTo(w * 0.75, h * 0.62)
|
||||||
|
..lineTo(w, h * 0.35) // نوکِ راست
|
||||||
|
..lineTo(w, h) // پایینراست
|
||||||
|
..close();
|
||||||
|
canvas.drawPath(p, _fill);
|
||||||
|
canvas.drawPath(p, _stroke);
|
||||||
|
// نگینهای نوکِ تاج
|
||||||
|
final gem = Paint()..color = const Color(0xFFB71C1C);
|
||||||
|
canvas.drawCircle(Offset(w * 0.5, h * 0.14), w * 0.05, gem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Path _starPath(Offset c, double outer, double inner, int points) {
|
||||||
|
final path = Path();
|
||||||
|
for (var i = 0; i < points * 2; i++) {
|
||||||
|
final rad = i.isEven ? outer : inner;
|
||||||
|
final ang = -math.pi / 2 + i * math.pi / points;
|
||||||
|
final pt = Offset(c.dx + rad * math.cos(ang), c.dy + rad * math.sin(ang));
|
||||||
|
i == 0 ? path.moveTo(pt.dx, pt.dy) : path.lineTo(pt.dx, pt.dy);
|
||||||
|
}
|
||||||
|
return path..close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// افکتِ ضربان (برای جلبِ توجه؛ مثلاً ستارهی نوبت).
|
||||||
|
PulseEffect pulse() => PulseEffect();
|
||||||
|
|
||||||
|
class PulseEffect extends ScaleEffect {
|
||||||
|
PulseEffect()
|
||||||
|
: super.by(
|
||||||
|
Vector2.all(0.25),
|
||||||
|
EffectController(
|
||||||
|
duration: 0.5,
|
||||||
|
reverseDuration: 0.5,
|
||||||
|
infinite: true,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,10 +9,12 @@ import 'package:flutter/material.dart';
|
|||||||
import '../../../../../core/service/app_sounds.dart';
|
import '../../../../../core/service/app_sounds.dart';
|
||||||
import '../../../domain/entities/game_entities.dart';
|
import '../../../domain/entities/game_entities.dart';
|
||||||
import '../../bloc/game_bloc.dart';
|
import '../../bloc/game_bloc.dart';
|
||||||
|
import 'badges.dart';
|
||||||
import 'card_codes.dart';
|
import 'card_codes.dart';
|
||||||
import 'card_component.dart';
|
import 'card_component.dart';
|
||||||
import 'shuffle_animation.dart';
|
import 'shuffle_animation.dart';
|
||||||
import 'table_pieces.dart';
|
import 'table_pieces.dart';
|
||||||
|
import 'turn_timer.dart';
|
||||||
|
|
||||||
/// صحنهی میز حکم با انیمیشن. شما همیشه پایین میز (rel=0) هستید.
|
/// صحنهی میز حکم با انیمیشن. شما همیشه پایین میز (rel=0) هستید.
|
||||||
/// کامپوننتهای دست و trick ماندگارند و با افکت حرکت جابهجا میشوند.
|
/// کامپوننتهای دست و trick ماندگارند و با افکت حرکت جابهجا میشوند.
|
||||||
@@ -46,6 +48,8 @@ class HokmGame extends FlameGame {
|
|||||||
bool _silent = false; // هنگام خروج از میز، دیگر صدایی پخش نشود
|
bool _silent = false; // هنگام خروج از میز، دیگر صدایی پخش نشود
|
||||||
bool _introActive = true; // تا پایانِ نمایشِ «جستجوی حریف»
|
bool _introActive = true; // تا پایانِ نمایشِ «جستجوی حریف»
|
||||||
bool _pendingShuffle = false; // بُرِ دستِ اول که تا پایانِ اینترو به تعویق افتاده
|
bool _pendingShuffle = false; // بُرِ دستِ اول که تا پایانِ اینترو به تعویق افتاده
|
||||||
|
bool _shuffling = false; // در حالِ پخشِ انیمیشنِ بُر زدن (بازیِ کارت ممنوع)
|
||||||
|
String? _prevTrump; // برای تشخیصِ لحظهی انتخابِ حکم
|
||||||
|
|
||||||
HokmGame(this.cubit);
|
HokmGame(this.cubit);
|
||||||
|
|
||||||
@@ -69,6 +73,12 @@ class HokmGame extends FlameGame {
|
|||||||
_shuffleNow();
|
_shuffleNow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// لحظهی انتخابِ حکم: صدا + نمایشِ بزرگِ حکم در مرکز.
|
||||||
|
if (_prevTrump == null && s.trump != null && !_silent) {
|
||||||
|
AppSounds.selectHokm();
|
||||||
|
_spawnTrumpReveal(s.trump!);
|
||||||
|
}
|
||||||
|
_prevTrump = s.trump;
|
||||||
_prevHandSize = s.yourHand.length;
|
_prevHandSize = s.yourHand.length;
|
||||||
_s = s;
|
_s = s;
|
||||||
_relayout();
|
_relayout();
|
||||||
@@ -76,6 +86,7 @@ class HokmGame extends FlameGame {
|
|||||||
if (cubit.state.state != null) {
|
if (cubit.state.state != null) {
|
||||||
_prevTrick = cubit.state.state!.trick.map((t) => t.card).toList();
|
_prevTrick = cubit.state.state!.trick.map((t) => t.card).toList();
|
||||||
_prevHandSize = cubit.state.state!.yourHand.length;
|
_prevHandSize = cubit.state.state!.yourHand.length;
|
||||||
|
_prevTrump = cubit.state.state!.trump;
|
||||||
_s = cubit.state.state;
|
_s = cubit.state.state;
|
||||||
_relayout();
|
_relayout();
|
||||||
}
|
}
|
||||||
@@ -138,10 +149,41 @@ class HokmGame extends FlameGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// اجرای همزمانِ انیمیشن و صدای بُر زدن (صدا با پایانِ انیمیشن قطع میشود).
|
/// اجرای همزمانِ انیمیشن و صدای بُر زدن (صدا با پایانِ انیمیشن قطع میشود).
|
||||||
|
/// در طولِ انیمیشن، بازیِ کارت قفل است تا کارتی وسطِ بُر زدن انداخته نشود.
|
||||||
void _shuffleNow() {
|
void _shuffleNow() {
|
||||||
if (_silent) return;
|
if (_silent) return;
|
||||||
AppSounds.shuffle(cutoffMs: ShuffleAnimation.totalMs);
|
AppSounds.shuffle(cutoffMs: ShuffleAnimation.totalMs);
|
||||||
add(ShuffleAnimation(size));
|
add(ShuffleAnimation(size));
|
||||||
|
_shuffling = true;
|
||||||
|
Future.delayed(const Duration(milliseconds: ShuffleAnimation.totalMs), () {
|
||||||
|
_shuffling = false;
|
||||||
|
if (_s != null && !_silent) _relayout(); // فعالسازیِ دوبارهی کارتها
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// نمایشِ بزرگ و گذرای حکمِ انتخابشده در مرکزِ میز (با پاپ).
|
||||||
|
void _spawnTrumpReveal(String trump) {
|
||||||
|
final (sym, col) = _trumpGlyph(trump);
|
||||||
|
final reveal = TextComponent(
|
||||||
|
text: 'حکم $sym',
|
||||||
|
anchor: Anchor.center,
|
||||||
|
position: size / 2 - Vector2(0, size.y * 0.10),
|
||||||
|
priority: 7000,
|
||||||
|
scale: Vector2.all(0.4),
|
||||||
|
textRenderer: TextPaint(
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'Dana',
|
||||||
|
color: col,
|
||||||
|
fontSize: size.x * 0.11,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
shadows: const [Shadow(color: Colors.black, blurRadius: 6)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
reveal.add(ScaleEffect.to(
|
||||||
|
Vector2.all(1.0), EffectController(duration: 0.35, curve: Curves.easeOutBack)));
|
||||||
|
reveal.add(RemoveEffect(delay: 1.3));
|
||||||
|
add(reveal);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _isPrefix(List<String> a, List<String> b) {
|
static bool _isPrefix(List<String> a, List<String> b) {
|
||||||
@@ -191,6 +233,8 @@ class HokmGame extends FlameGame {
|
|||||||
// آیا این کارت در نوبتِ فعلی قابل بازی است (با رعایت follow-suit)؟
|
// آیا این کارت در نوبتِ فعلی قابل بازی است (با رعایت follow-suit)؟
|
||||||
bool _legal(String code) {
|
bool _legal(String code) {
|
||||||
final s = _s!;
|
final s = _s!;
|
||||||
|
// هنگامِ نمایشِ اینترو یا انیمیشنِ بُر زدن، بازیِ کارت مجاز نیست.
|
||||||
|
if (_introActive || _shuffling) return false;
|
||||||
if (s.phase != 'playing' || s.trickDone || !s.isMyTurn) return false;
|
if (s.phase != 'playing' || s.trickDone || !s.isMyTurn) return false;
|
||||||
final lead = s.leadSuit;
|
final lead = s.leadSuit;
|
||||||
if (lead == null || lead.isEmpty) return true;
|
if (lead == null || lead.isEmpty) return true;
|
||||||
@@ -441,15 +485,33 @@ class HokmGame extends FlameGame {
|
|||||||
color: isTurn ? const Color(0xFFE9B949) : Colors.white70,
|
color: isTurn ? const Color(0xFFE9B949) : Colors.white70,
|
||||||
bold: isTurn,
|
bold: isTurn,
|
||||||
);
|
);
|
||||||
|
// تاجِ حاکم (روی همهی فازها نشان داده میشود تا حاکم مشخص باشد).
|
||||||
|
if (s.hakem == p.seat) {
|
||||||
|
final crown = CrownBadge(size.x * 0.06, pos - Vector2(0, size.y * 0.066))
|
||||||
|
..priority = 23;
|
||||||
|
_info.add(crown);
|
||||||
|
add(crown);
|
||||||
|
}
|
||||||
if (isTurn) {
|
if (isTurn) {
|
||||||
final dot = CircleComponent(
|
final tpos = pos - Vector2(0, size.y * 0.03);
|
||||||
radius: size.x * 0.012,
|
final human = !p.bot && p.connected;
|
||||||
anchor: Anchor.center,
|
// حلقهی شمارشِ نوبت برای بازیکنِ انسان.
|
||||||
position: pos - Vector2(0, size.y * 0.03),
|
if (human &&
|
||||||
paint: Paint()..color = const Color(0xFFE9B949),
|
s.turnTimeoutMs > 0 &&
|
||||||
|
(s.phase == 'playing' || s.phase == 'choose_trump')) {
|
||||||
|
final timer = TurnTimer(
|
||||||
|
radius: size.x * 0.03,
|
||||||
|
durationSeconds: s.turnTimeoutMs / 1000.0,
|
||||||
|
position: tpos,
|
||||||
)..priority = 20;
|
)..priority = 20;
|
||||||
_info.add(dot);
|
_info.add(timer);
|
||||||
add(dot);
|
add(timer);
|
||||||
|
}
|
||||||
|
// ستارهی نوبت (مطابقِ تصویرِ مرجع) با ضربان.
|
||||||
|
final star = StarBadge(size.x * 0.015, tpos)..priority = 24;
|
||||||
|
star.add(pulse());
|
||||||
|
_info.add(star);
|
||||||
|
add(star);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// حلقهی شمارشِ معکوسِ نوبت: کمانی که در طولِ مهلتِ نوبت خالی میشود.
|
||||||
|
/// رنگ از طلایی به قرمز میرود و در ثانیههای پایانی هشدار میدهد.
|
||||||
|
class TurnTimer extends PositionComponent {
|
||||||
|
final double radius;
|
||||||
|
final double durationSeconds;
|
||||||
|
double _elapsed = 0;
|
||||||
|
|
||||||
|
TurnTimer({
|
||||||
|
required this.radius,
|
||||||
|
required this.durationSeconds,
|
||||||
|
required Vector2 position,
|
||||||
|
}) : super(
|
||||||
|
position: position,
|
||||||
|
size: Vector2.all(radius * 2),
|
||||||
|
anchor: Anchor.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
static final _bg = Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 3
|
||||||
|
..color = Colors.black54;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(double dt) {
|
||||||
|
_elapsed += dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void render(Canvas canvas) {
|
||||||
|
final center = Offset(radius, radius);
|
||||||
|
canvas.drawCircle(center, radius, _bg);
|
||||||
|
|
||||||
|
final frac =
|
||||||
|
durationSeconds <= 0 ? 0.0 : (1 - _elapsed / durationSeconds).clamp(0.0, 1.0);
|
||||||
|
if (frac <= 0) return;
|
||||||
|
|
||||||
|
final color = frac > 0.3 ? const Color(0xFFE9B949) : const Color(0xFFE53935);
|
||||||
|
final arc = Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 4
|
||||||
|
..strokeCap = StrokeCap.round
|
||||||
|
..color = color;
|
||||||
|
canvas.drawArc(
|
||||||
|
Rect.fromCircle(center: center, radius: radius),
|
||||||
|
-math.pi / 2, // شروع از بالا
|
||||||
|
2 * math.pi * frac,
|
||||||
|
false,
|
||||||
|
arc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user