screen-locker/stronglift_replacement/workout_app/lib/widgets/rep_circle.dart
Krzysztof kuhy Rudnicki 26d55fb311 feat: add Flutter workout app (StrongLifts replacement)
Full-featured workout tracker with session persistence, auto A/B cycling,
warmup weights (4/5 of target), settings weight stepper, history + progress
graph, HTTP sync server, and crash-safe active session resume.

Removed per-set break timers per user preference. Dropped audioplayers and
vibration dependencies; updated permission_handler to 12.x to eliminate
two of three KGP build warnings (shared_preferences_android is an upstream issue).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:23:46 +02:00

107 lines
2.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// Tappable circle widget representing one set of an exercise.
///
/// States:
/// neutral – white, shows target reps, not yet acted upon
/// success – green, shows target reps (full set done)
/// partial – orange, shows how many reps were actually done (< target)
/// failed – red, shows 0 (all reps deducted)
///
/// Interaction:
/// single tap → neutral→success, success→partial(-1 rep), partial→partial(-1 rep),
/// failed stays failed
/// long press → reset to neutral
library;
import 'package:flutter/material.dart';
enum RepCircleState { neutral, success, partial, failed }
class RepCircle extends StatelessWidget {
const RepCircle({
super.key,
required this.targetReps,
required this.doneReps,
required this.tapped,
required this.onTap,
required this.onLongPress,
});
final int targetReps;
/// Reps currently registered (may be < targetReps if user tapped multiple times).
final int doneReps;
/// Whether this circle has been tapped at all (distinguishes neutral from success).
final bool tapped;
final VoidCallback onTap;
final VoidCallback onLongPress;
RepCircleState get _state {
if (!tapped) return RepCircleState.neutral;
if (doneReps >= targetReps) return RepCircleState.success;
if (doneReps > 0) return RepCircleState.partial;
return RepCircleState.failed;
}
@override
Widget build(BuildContext context) {
final state = _state;
final Color bg;
final Color fg;
final String label;
switch (state) {
case RepCircleState.neutral:
bg = Colors.white;
fg = Colors.black87;
label = '$targetReps';
case RepCircleState.success:
bg = Colors.green;
fg = Colors.white;
label = '$targetReps';
case RepCircleState.partial:
bg = Colors.orange;
fg = Colors.white;
label = '$doneReps';
case RepCircleState.failed:
bg = Colors.red;
fg = Colors.white;
label = '0';
}
return GestureDetector(
onTap: onTap,
onLongPress: onLongPress,
child: Container(
width: 52,
height: 52,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: bg,
border: Border.all(
color: state == RepCircleState.neutral ? Colors.grey.shade400 : bg,
width: 2,
),
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 4,
offset: Offset(0, 2),
),
],
),
alignment: Alignment.center,
child: Text(
label,
style: TextStyle(
color: fg,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
);
}
}