feat: add assisted exercise an next set preview

This commit is contained in:
Patryk Hegenberg 2026-01-06 10:29:43 +01:00
parent 44f5703de4
commit 79a7e1c50d
10 changed files with 534 additions and 270 deletions

4
.gitignore vendored
View file

@ -43,3 +43,7 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
.env
.env.production
.env.development

View file

@ -10,11 +10,11 @@ void main() async {
try { try {
await dotenv.load(fileName: '.env'); await dotenv.load(fileName: '.env');
debugPrint('Environment loaded: ${dotenv.env['ENVIRONMENT']}'); debugPrint('Environment loaded: ${dotenv.env['ENVIRONMENT']}');
debugPrint('API URL: ${dotenv.env['API_BASE_URL']}'); debugPrint('API URL: ${dotenv.env['API_BASE_URL']}');
} catch (e) { } catch (e) {
debugPrint('⚠️ Could not load .env file: $e'); debugPrint('Could not load .env file: $e');
debugPrint('⚠️ Using default production values'); debugPrint('Using default production values');
} }
await SystemChrome.setPreferredOrientations([ await SystemChrome.setPreferredOrientations([

View file

@ -425,7 +425,8 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
), ),
body: _isLoading body: _isLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: ListView( : SafeArea(
child: ListView(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
children: [ children: [
Center( Center(
@ -560,8 +561,8 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
color: AppTheme.errorColor), color: AppTheme.errorColor),
title: const Text('Reset Progress', title: const Text('Reset Progress',
style: TextStyle(color: AppTheme.errorColor)), style: TextStyle(color: AppTheme.errorColor)),
subtitle: subtitle: const Text(
const Text('Resets Level, XP and Training History'), 'Resets Level, XP and Training History'),
onTap: () => _confirmDangerAction( onTap: () => _confirmDangerAction(
'Reset Progress?', 'Reset Progress?',
'This will delete all your workouts and reset your Level to 1. This cannot be undone.', 'This will delete all your workouts and reset your Level to 1. This cannot be undone.',
@ -617,8 +618,12 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
), ),
), ),
const SizedBox(
height: 50,
)
], ],
), ),
),
); );
} }

View file

@ -32,6 +32,9 @@ class _StrengthTestScreenState extends ConsumerState<StrengthTestScreen> {
Map<String, double> _calculated1RMs = {}; Map<String, double> _calculated1RMs = {};
Map<String, double> _calculatedTMs = {}; Map<String, double> _calculatedTMs = {};
bool _isAssistedPull = false;
bool _isAssistedDip = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -52,16 +55,27 @@ class _StrengthTestScreenState extends ConsumerState<StrengthTestScreen> {
void _calculateAll() { void _calculateAll() {
final bodyweight = ref.read(onboardingDataProvider)['bodyweight'] ?? 80.0; final bodyweight = ref.read(onboardingDataProvider)['bodyweight'] ?? 80.0;
// Squat bleibt gleich...
final squatWeight = double.tryParse(_squatWeightController.text) ?? 0; final squatWeight = double.tryParse(_squatWeightController.text) ?? 0;
final squatReps = int.tryParse(_squatRepsController.text) ?? 1; final squatReps = int.tryParse(_squatRepsController.text) ?? 1;
final squat1RM = WendlerCalculator.calculate1RM(squatWeight, squatReps); final squat1RM = WendlerCalculator.calculate1RM(squatWeight, squatReps);
final squatTM = WendlerCalculator.calculateTrainingMax(squat1RM); final squatTM = WendlerCalculator.calculateTrainingMax(squat1RM);
// PULL CALCULATION (Angepasst)
double pull1RM = 0.0; double pull1RM = 0.0;
if (_canDoPullup) { if (_canDoPullup) {
final added = double.tryParse(_pullWeightController.text) ?? 0; final inputWeight = double.tryParse(_pullWeightController.text) ?? 0;
final reps = int.tryParse(_pullRepsController.text) ?? 1; final reps = int.tryParse(_pullRepsController.text) ?? 1;
pull1RM = WendlerCalculator.calculate1RM(bodyweight + added, reps);
// LOGIK: Assisted vs Weighted
double totalLoad;
if (_isAssistedPull) {
totalLoad = (bodyweight - inputWeight).clamp(0.0, double.infinity);
} else {
totalLoad = bodyweight + inputWeight;
}
pull1RM = WendlerCalculator.calculate1RM(totalLoad, reps);
} else { } else {
final weight = double.tryParse(_pullWeightController.text) ?? 0; final weight = double.tryParse(_pullWeightController.text) ?? 0;
final reps = int.tryParse(_pullRepsController.text) ?? 1; final reps = int.tryParse(_pullRepsController.text) ?? 1;
@ -69,11 +83,21 @@ class _StrengthTestScreenState extends ConsumerState<StrengthTestScreen> {
} }
final pullTM = WendlerCalculator.calculateTrainingMax(pull1RM); final pullTM = WendlerCalculator.calculateTrainingMax(pull1RM);
// PUSH CALCULATION (Angepasst)
double push1RM = 0.0; double push1RM = 0.0;
if (_canDoDip) { if (_canDoDip) {
final added = double.tryParse(_dipWeightController.text) ?? 0; final inputWeight = double.tryParse(_dipWeightController.text) ?? 0;
final reps = int.tryParse(_pushRepsController.text) ?? 1; final reps = int.tryParse(_pushRepsController.text) ?? 1;
push1RM = WendlerCalculator.calculate1RM(bodyweight + added, reps);
// LOGIK: Assisted vs Weighted
double totalLoad;
if (_isAssistedDip) {
totalLoad = (bodyweight - inputWeight).clamp(0.0, double.infinity);
} else {
totalLoad = bodyweight + inputWeight;
}
push1RM = WendlerCalculator.calculate1RM(totalLoad, reps);
} else { } else {
final weight = double.tryParse(_benchWeightController.text) ?? 0; final weight = double.tryParse(_benchWeightController.text) ?? 0;
final reps = int.tryParse(_pushRepsController.text) ?? 1; final reps = int.tryParse(_pushRepsController.text) ?? 1;
@ -95,6 +119,52 @@ class _StrengthTestScreenState extends ConsumerState<StrengthTestScreen> {
}); });
} }
// void _calculateAll() {
// final bodyweight = ref.read(onboardingDataProvider)['bodyweight'] ?? 80.0;
// final squatWeight = double.tryParse(_squatWeightController.text) ?? 0;
// final squatReps = int.tryParse(_squatRepsController.text) ?? 1;
// final squat1RM = WendlerCalculator.calculate1RM(squatWeight, squatReps);
// final squatTM = WendlerCalculator.calculateTrainingMax(squat1RM);
// double pull1RM = 0.0;
// if (_canDoPullup) {
// final added = double.tryParse(_pullWeightController.text) ?? 0;
// final reps = int.tryParse(_pullRepsController.text) ?? 1;
// pull1RM = WendlerCalculator.calculate1RM(bodyweight + added, reps);
// } else {
// final weight = double.tryParse(_pullWeightController.text) ?? 0;
// final reps = int.tryParse(_pullRepsController.text) ?? 1;
// pull1RM = WendlerCalculator.calculate1RM(weight, reps);
// }
// final pullTM = WendlerCalculator.calculateTrainingMax(pull1RM);
// double push1RM = 0.0;
// if (_canDoDip) {
// final added = double.tryParse(_dipWeightController.text) ?? 0;
// final reps = int.tryParse(_pushRepsController.text) ?? 1;
// push1RM = WendlerCalculator.calculate1RM(bodyweight + added, reps);
// } else {
// final weight = double.tryParse(_benchWeightController.text) ?? 0;
// final reps = int.tryParse(_pushRepsController.text) ?? 1;
// push1RM = WendlerCalculator.calculate1RM(weight, reps);
// }
// final pushTM = WendlerCalculator.calculateTrainingMax(push1RM);
// setState(() {
// _calculated1RMs = {
// 'squat': squat1RM,
// 'pullup': pull1RM,
// 'dip': push1RM,
// };
// _calculatedTMs = {
// 'squat': squatTM,
// 'pullup': pullTM,
// 'dip': pushTM,
// };
// });
// }
void _handleContinue() { void _handleContinue() {
if (!_formKey.currentState!.validate()) return; if (!_formKey.currentState!.validate()) return;
@ -171,12 +241,24 @@ class _StrengthTestScreenState extends ConsumerState<StrengthTestScreen> {
_calculateAll(); _calculateAll();
}); });
}, },
isAssisted: _isAssistedPull,
onToggleAssisted: (val) {
setState(() {
_isAssistedPull = val;
_calculateAll();
});
},
weightController: _pullWeightController, weightController: _pullWeightController,
repsController: _pullRepsController, repsController: _pullRepsController,
weightLabel: weightLabel: _canDoPullup
_canDoPullup ? 'Add. Weight (kg)' : 'Row Weight (kg)', ? (_isAssistedPull
? 'Band Assistance (kg)'
: 'Added Weight (kg)')
: 'Row Weight (kg)',
// weightLabel:
// _canDoPullup ? 'Add. Weight (kg)' : 'Row Weight (kg)',
repsLabel: _canDoPullup ? 'Reps' : '5RM Reps (usually 5)', repsLabel: _canDoPullup ? 'Reps' : '5RM Reps (usually 5)',
showResults: _canDoPullup || true, showResults: true,
result1RM: _calculated1RMs['pullup'] ?? 0, result1RM: _calculated1RMs['pullup'] ?? 0,
resultTM: _calculatedTMs['pullup'] ?? 0, resultTM: _calculatedTMs['pullup'] ?? 0,
onChanged: _calculateAll, onChanged: _calculateAll,
@ -196,10 +278,22 @@ class _StrengthTestScreenState extends ConsumerState<StrengthTestScreen> {
_calculateAll(); _calculateAll();
}); });
}, },
isAssisted: _isAssistedDip,
onToggleAssisted: (val) {
setState(() {
_isAssistedDip = val;
_calculateAll();
});
},
weightController: weightController:
_canDoDip ? _dipWeightController : _benchWeightController, _canDoDip ? _dipWeightController : _benchWeightController,
repsController: _pushRepsController, repsController: _pushRepsController,
weightLabel: _canDoDip ? 'Add. Weight (kg)' : 'Weight (kg)', weightLabel: _canDoDip
? (_isAssistedDip
? 'Band Assistance (kg)'
: 'Added Weight (kg)')
: 'Weight (kg)',
// weightLabel: _canDoDip ? 'Add. Weight (kg)' : 'Weight (kg)',
repsLabel: 'Reps', repsLabel: 'Reps',
showWeightInput: true, showWeightInput: true,
showResults: true, showResults: true,
@ -295,7 +389,10 @@ class _ExerciseCard extends StatelessWidget {
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Text(exerciseName, Text(exerciseName,
style: Theme.of(context).textTheme.titleLarge), style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: AppTheme.textPrimary)),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@ -357,6 +454,8 @@ class _AdaptiveExerciseCard extends StatelessWidget {
final double result1RM; final double result1RM;
final double resultTM; final double resultTM;
final VoidCallback onChanged; final VoidCallback onChanged;
final bool isAssisted;
final ValueChanged<bool>? onToggleAssisted;
const _AdaptiveExerciseCard({ const _AdaptiveExerciseCard({
required this.slotTitle, required this.slotTitle,
@ -374,6 +473,8 @@ class _AdaptiveExerciseCard extends StatelessWidget {
required this.result1RM, required this.result1RM,
required this.resultTM, required this.resultTM,
required this.onChanged, required this.onChanged,
this.isAssisted = false,
this.onToggleAssisted,
}); });
@override @override
@ -384,14 +485,15 @@ class _AdaptiveExerciseCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(slotTitle.toUpperCase(), Text(slotTitle.toUpperCase(),
style: const TextStyle( style: const TextStyle(
color: AppTheme.textSecondary, color: AppTheme.textSecondary,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
]),
Row(
children: [
Row( Row(
children: [ children: [
Text('Can do 1 rep?', Text('Can do 1 rep?',
@ -407,6 +509,24 @@ class _AdaptiveExerciseCard extends StatelessWidget {
), ),
], ],
), ),
if (isCapable && onToggleAssisted != null)
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('Assisted (Bands)?',
style: TextStyle(
fontSize: 12,
color: isAssisted
? AppTheme.primaryColor
: Colors.grey)),
Switch(
value: isAssisted,
activeThumbColor: AppTheme.primaryColor,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: onToggleAssisted,
),
],
),
], ],
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
@ -421,13 +541,16 @@ class _AdaptiveExerciseCard extends StatelessWidget {
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Text(isCapable ? primaryName : secondaryName, Text(isCapable ? primaryName : secondaryName,
style: Theme.of(context).textTheme.titleLarge), style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: AppTheme.textPrimary)),
], ],
), ),
if (!isCapable) ...[ if (!isCapable) ...[
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'Adjusted Strategy: ${isCapable ? "Wendler 5/3/1" : "Linear Progression (3x5)"}', 'Adjusted: ${"Wendler 5/3/1"}',
style: const TextStyle( style: const TextStyle(
color: AppTheme.secondaryColor, color: AppTheme.secondaryColor,
fontSize: 12, fontSize: 12,

View file

@ -352,10 +352,14 @@ class _CurrentCycleCard extends StatelessWidget {
Text('Current Training Maxes (TM)', Text('Current Training Maxes (TM)',
style: Theme.of(context).textTheme.labelLarge), style: Theme.of(context).textTheme.labelLarge),
const SizedBox(height: 16), const SizedBox(height: 16),
_StatRow(label: 'Squat', value: '${tms['squat']} kg'),
_StatRow( _StatRow(
label: getLabel(pullVariant), value: '${tms['pullup']} kg'), label: 'Squat', value: '${tms['squat'].toStringAsFixed(2)} kg'),
_StatRow(label: getLabel(pushVariant), value: '${tms['dip']} kg'), _StatRow(
label: getLabel(pullVariant),
value: '${tms['pullup'].toStringAsFixed(2)} kg'),
_StatRow(
label: getLabel(pushVariant),
value: '${tms['dip'].toStringAsFixed(2)} kg'),
const SizedBox(height: 32), const SizedBox(height: 32),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
@ -468,15 +472,15 @@ class _DiffRow extends StatelessWidget {
child: Row( child: Row(
children: [ children: [
Expanded(child: Text(name)), Expanded(child: Text(name)),
Text('${oldVal.toStringAsFixed(1)}', Text('${oldVal.toStringAsFixed(2)}',
style: const TextStyle(color: Colors.grey)), style: const TextStyle(color: Colors.grey)),
Text( Text(
newVal.toStringAsFixed(1), newVal.toStringAsFixed(2),
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
if (isPositive) if (isPositive)
Text('+${diff.toStringAsFixed(1)}', Text('+${diff.toStringAsFixed(2)}',
style: const TextStyle( style: const TextStyle(
color: AppTheme.successColor, fontWeight: FontWeight.bold)) color: AppTheme.successColor, fontWeight: FontWeight.bold))
else else

View file

@ -67,22 +67,14 @@ class WorkoutGeneratorService {
List<WorkoutSet> sets; List<WorkoutSet> sets;
if (isMain) { if (isMain) {
if (type == ExerciseType.row || type == ExerciseType.bench) {
sets = WendlerCalculator.generateLinearSets(
trainingMax: tm,
exerciseType: type,
currentBodyweight: user.currentBodyweight);
} else {
sets = WendlerCalculator.generateSets( sets = WendlerCalculator.generateSets(
week: week, week: week,
trainingMax: tm, trainingMax: tm,
exerciseType: type, exerciseType: type,
currentBodyweight: user.currentBodyweight, currentBodyweight: user.currentBodyweight,
); );
}
} else { } else {
if (week == 4) return; if (week == 4) return;
if (type == ExerciseType.row || type == ExerciseType.bench) return;
sets = WendlerCalculator.generateFSLSets( sets = WendlerCalculator.generateFSLSets(
trainingMax: tm, trainingMax: tm,
@ -156,8 +148,8 @@ class WorkoutGeneratorService {
weight: calculateWeight(squatTm, 0.4))); weight: calculateWeight(squatTm, 0.4)));
accessories.add(_createIntervalExercise( accessories.add(_createIntervalExercise(
id: 'kb_swing', id: 'kb_snatch_acc',
name: '2H KB Swing', name: 'KB Snatch',
sets: 10, sets: 10,
intervalSeconds: 60, intervalSeconds: 60,
repsPerSet: 10)); repsPerSet: 10));
@ -178,8 +170,8 @@ class WorkoutGeneratorService {
weight: calculateWeight(pullupTm, 0.2))); weight: calculateWeight(pullupTm, 0.2)));
accessories.add(_createIntervalExercise( accessories.add(_createIntervalExercise(
id: 'kb_snatch_acc', id: 'kb_swing',
name: 'KB Snatch', name: '2H KB Swing',
sets: 10, sets: 10,
intervalSeconds: 60, intervalSeconds: 60,
repsPerSet: 5)); repsPerSet: 5));

View file

@ -19,6 +19,7 @@ import '../widgets/plate_visualizer.dart';
import '../widgets/enemy_hp_bar.dart'; import '../widgets/enemy_hp_bar.dart';
import '../../../gamification/application/quest_service.dart'; import '../../../gamification/application/quest_service.dart';
import '../widgets/emom_timer_widget.dart'; import '../widgets/emom_timer_widget.dart';
import '../widgets/timer_widget.dart';
class BattleScreen extends ConsumerStatefulWidget { class BattleScreen extends ConsumerStatefulWidget {
final int week; final int week;
@ -615,7 +616,7 @@ class _BattleScreenState extends ConsumerState<BattleScreen> {
Positioned.fill( Positioned.fill(
child: SafeArea( child: SafeArea(
child: _isResting child: _isResting
? _buildRestScreen() ? _buildRestScreen(inventory)
: _buildWorkoutScreen(currentExercise, currentSet, : _buildWorkoutScreen(currentExercise, currentSet,
plateResult, completedHP, totalHP), plateResult, completedHP, totalHP),
), ),
@ -626,7 +627,20 @@ class _BattleScreenState extends ConsumerState<BattleScreen> {
}); });
} }
Widget _buildRestScreen() { Widget _buildRestScreen(Map<String, dynamic> inventory) {
WorkoutSet? nextSet;
Exercise? nextExerciseInfo;
if (_currentSetIndex + 1 < _exercises[_currentExerciseIndex].sets.length) {
nextExerciseInfo = _exercises[_currentExerciseIndex];
nextSet = nextExerciseInfo.sets[_currentSetIndex + 1];
} else if (_currentExerciseIndex + 1 < _exercises.length) {
nextExerciseInfo = _exercises[_currentExerciseIndex + 1];
if (nextExerciseInfo.sets.isNotEmpty) {
nextSet = nextExerciseInfo.sets.first;
}
}
return Container( return Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
@ -639,14 +653,17 @@ class _BattleScreenState extends ConsumerState<BattleScreen> {
), ),
), ),
child: Center( child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
'REST', 'REST',
style: Theme.of(context).textTheme.displayLarge, style: Theme.of(context).textTheme.displayLarge,
), ),
const SizedBox(height: 32), const SizedBox(height: 20),
SizedBox( SizedBox(
width: 200, width: 200,
height: 200, height: 200,
@ -666,24 +683,121 @@ class _BattleScreenState extends ConsumerState<BattleScreen> {
Text( Text(
_formatTime(_restSeconds), _formatTime(_restSeconds),
style: Theme.of(context).textTheme.displayLarge?.copyWith( style: Theme.of(context).textTheme.displayLarge?.copyWith(
fontSize: 48, fontSize: 32,
color: AppTheme.primaryColor, color: AppTheme.primaryColor,
), ),
), ),
], ],
), ),
), ),
const SizedBox(height: 48), const SizedBox(height: 20),
ElevatedButton( ElevatedButton(
onPressed: _skipRest, onPressed: _skipRest,
child: const Text('SKIP REST'), child: const Text('SKIP REST'),
), ),
if (nextSet != null && nextExerciseInfo != null) ...[
const SizedBox(height: 24),
const Divider(color: Colors.white10, endIndent: 32, indent: 32),
const SizedBox(height: 12),
Text(
'UP NEXT: ${nextExerciseInfo.exerciseName.toUpperCase()}',
style: const TextStyle(
color: Colors.grey,
fontSize: 11,
fontWeight: FontWeight.bold,
letterSpacing: 1.2),
),
const SizedBox(height: 4),
Text(
'${nextSet.repsTarget} x ${nextSet.targetWeightTotal > 0 ? "${nextSet.targetWeightTotal} kg" : "Bodyweight"}',
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
if (nextSet.targetWeightTotal > 0)
_buildNextSetPlates(nextExerciseInfo, nextSet, inventory),
], ],
],
),
), ),
), ),
); );
} }
Widget _buildNextSetPlates(
Exercise exercise, WorkoutSet set, Map<String, dynamic> inventory) {
final isTwoSided = exercise.exerciseId == 'squat' ||
exercise.exerciseId == 'row' ||
exercise.exerciseId == 'bench' ||
exercise.exerciseId == 'rdl' ||
exercise.exerciseId == 'ohp' ||
exercise.exerciseId == 'curl';
if (!isTwoSided) return const SizedBox.shrink();
final barWeight = (inventory['bar_weight'] as num?)?.toDouble() ?? 20.0;
final platesList = (inventory['plates'] as List?)
?.map((e) => (e as num).toDouble())
.toList() ??
[];
final plateResult = PlateCalculator.calculate(
targetWeight: set.targetWeightTotal,
barWeight: barWeight,
availablePlates: platesList,
availableBands: {},
isTwoSided: true,
);
return Padding(
padding: const EdgeInsets.only(top: 12.0),
child: PlateVisualizer(
plateConfiguration: plateResult.plateConfiguration,
isTwoSided: true,
exerciseName: '',
),
);
}
// Widget _buildNextSetPlates(
// Exercise exercise, WorkoutSet set, Map<String, dynamic> inventory) {
// final isTwoSided = exercise.exerciseId == 'squat' ||
// exercise.exerciseId == 'row' ||
// exercise.exerciseId == 'bench' ||
// exercise.exerciseId == 'rdl' ||
// exercise.exerciseId == 'ohp' ||
// exercise.exerciseId == 'curl';
// if (!isTwoSided) return const SizedBox.shrink();
// final barWeight = (inventory['bar_weight'] as num?)?.toDouble() ?? 20.0;
// final platesList = (inventory['plates'] as List?)
// ?.map((e) => (e as num).toDouble())
// .toList() ??
// [];
// final plateResult = PlateCalculator.calculate(
// targetWeight: set.targetWeightTotal,
// barWeight: barWeight,
// availablePlates: platesList,
// availableBands: {},
// isTwoSided: true,
// );
// return Padding(
// padding: const EdgeInsets.only(top: 12.0),
// child: SizedBox(
// height: 50,
// child: PlateVisualizer(
// plateConfiguration: plateResult.plateConfiguration,
// isTwoSided: true,
// exerciseName: '',
// ),
// ),
// );
// }
Widget _buildWorkoutScreen( Widget _buildWorkoutScreen(
Exercise currentExercise, Exercise currentExercise,
WorkoutSet currentSet, WorkoutSet currentSet,

View file

@ -38,6 +38,24 @@ class _EmomTimerWidgetState extends State<EmomTimerWidget>
_secondsRemaining = widget.intervalSeconds; _secondsRemaining = widget.intervalSeconds;
_audioPlayer = AudioPlayer(); _audioPlayer = AudioPlayer();
_audioPlayer.setAudioContext(
AudioContext(
android: AudioContextAndroid(
isSpeakerphoneOn: false,
stayAwake: false,
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.notificationEvent,
audioFocus: AndroidAudioFocus.none,
),
// iOS: AudioContextIOS(
// category: AVAudioSessionCategory.ambient,
// options: [
// AVAudioSessionOptions.mixWithOthers,
// ],
// ),
),
);
_pulseController = AnimationController( _pulseController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),

View file

@ -238,7 +238,7 @@ class PlateVisualizer extends StatelessWidget {
if (isTwoSided) _buildBarbellView() else _buildBeltView(), if (isTwoSided) _buildBarbellView() else _buildBeltView(),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'Total: ${plateConfiguration.fold<double>(0, (sum, p) => sum + p).toStringAsFixed(1)} kg ${isTwoSided ? 'per side' : ''}', 'Total: ${plateConfiguration.fold<double>(0, (sum, p) => sum + p).toStringAsFixed(2)} kg ${isTwoSided ? 'per side' : ''}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: AppTheme.primaryColor, color: AppTheme.primaryColor,
), ),

View file

@ -62,7 +62,9 @@ class WendlerCalculator {
final rounded = _roundWeight(targetTotal, exerciseType); final rounded = _roundWeight(targetTotal, exerciseType);
double plateWeight = 0; double plateWeight = 0;
if (exerciseType != ExerciseType.squat) { if (exerciseType != ExerciseType.squat ||
exerciseType != ExerciseType.row ||
exerciseType != ExerciseType.bench) {
plateWeight = max(0, rounded - currentBodyweight); plateWeight = max(0, rounded - currentBodyweight);
} }
@ -144,7 +146,9 @@ class WendlerCalculator {
final rounded = _roundWeight(targetTotal, exerciseType); final rounded = _roundWeight(targetTotal, exerciseType);
double plateWeight = 0; double plateWeight = 0;
if (exerciseType != ExerciseType.squat) { if (exerciseType != ExerciseType.squat ||
exerciseType != ExerciseType.row ||
exerciseType != ExerciseType.bench) {
plateWeight = max(0, rounded - currentBodyweight); plateWeight = max(0, rounded - currentBodyweight);
} }