Przebudowanie aplikacji, usprawnione AI, dodanie combo buildera
Some checks failed
Build Linux App / build (push) Failing after 1m18s
Some checks failed
Build Linux App / build (push) Failing after 1m18s
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:trainhub_flutter/core/theme/app_colors.dart';
|
||||
import 'package:trainhub_flutter/core/constants/ui_constants.dart';
|
||||
import 'package:trainhub_flutter/domain/entities/workout_activity.dart';
|
||||
@@ -21,9 +22,7 @@ class ActivityCard extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceContainer.withValues(alpha: 0.6),
|
||||
borderRadius: UIConstants.cardBorderRadius,
|
||||
border: Border.all(
|
||||
color: accentColor.withValues(alpha: 0.2),
|
||||
),
|
||||
border: Border.all(color: accentColor.withValues(alpha: 0.2)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -47,11 +46,12 @@ class ActivityCard extends StatelessWidget {
|
||||
const SizedBox(height: UIConstants.spacing12),
|
||||
// Activity name
|
||||
Text(
|
||||
activity.name,
|
||||
style: const TextStyle(
|
||||
activity.name.toUpperCase(),
|
||||
style: GoogleFonts.chakraPetch(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -59,10 +59,7 @@ class ActivityCard extends StatelessWidget {
|
||||
const SizedBox(height: UIConstants.spacing8),
|
||||
Text(
|
||||
'${activity.sectionName ?? ''} \u00B7 Set ${activity.setIndex}/${activity.totalSets}',
|
||||
style: const TextStyle(
|
||||
color: AppColors.textMuted,
|
||||
fontSize: 13,
|
||||
),
|
||||
style: const TextStyle(color: AppColors.textMuted, fontSize: 13),
|
||||
),
|
||||
if (activity.originalExercise != null) ...[
|
||||
const SizedBox(height: UIConstants.spacing16),
|
||||
@@ -86,10 +83,7 @@ class ActivityCard extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(top: UIConstants.spacing8),
|
||||
child: Text(
|
||||
'Take a break',
|
||||
style: TextStyle(
|
||||
color: AppColors.textMuted,
|
||||
fontSize: 14,
|
||||
),
|
||||
style: TextStyle(color: AppColors.textMuted, fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -124,10 +118,7 @@ class _InfoChip extends StatelessWidget {
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: AppColors.textMuted,
|
||||
fontSize: 11,
|
||||
),
|
||||
style: const TextStyle(color: AppColors.textMuted, fontSize: 11),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -46,11 +46,7 @@ class SessionControls extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isTimeBased) ...[
|
||||
_ControlButton(
|
||||
icon: Icons.replay_10,
|
||||
onTap: onRewind,
|
||||
size: 24,
|
||||
),
|
||||
_ControlButton(icon: Icons.replay_10, onTap: onRewind, size: 24),
|
||||
const SizedBox(width: UIConstants.spacing8),
|
||||
],
|
||||
_ControlButton(
|
||||
|
||||
@@ -101,9 +101,7 @@ class WorkoutSessionController extends _$WorkoutSessionController {
|
||||
0,
|
||||
maxDuration,
|
||||
);
|
||||
state = AsyncValue.data(
|
||||
currentState.copyWith(timeRemaining: newRemaining),
|
||||
);
|
||||
state = AsyncValue.data(currentState.copyWith(timeRemaining: newRemaining));
|
||||
}
|
||||
|
||||
void _tick(Timer timer) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:math' as math;
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:trainhub_flutter/core/constants/ui_constants.dart';
|
||||
import 'package:trainhub_flutter/core/theme/app_colors.dart';
|
||||
import 'package:trainhub_flutter/domain/entities/workout_activity.dart';
|
||||
@@ -25,7 +26,7 @@ class WorkoutSessionPage extends ConsumerWidget {
|
||||
final asyncState = ref.watch(workoutSessionControllerProvider(planId));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.zinc950,
|
||||
backgroundColor: AppColors.surface,
|
||||
body: asyncState.when(
|
||||
loading: () => const Center(
|
||||
child: CircularProgressIndicator(
|
||||
@@ -71,15 +72,10 @@ class WorkoutSessionPage extends ConsumerWidget {
|
||||
);
|
||||
|
||||
if (state.isFinished) {
|
||||
return _CompletionScreen(
|
||||
totalTimeElapsed: state.totalTimeElapsed,
|
||||
);
|
||||
return _CompletionScreen(totalTimeElapsed: state.totalTimeElapsed);
|
||||
}
|
||||
|
||||
return _ActiveSessionView(
|
||||
state: state,
|
||||
controller: controller,
|
||||
);
|
||||
return _ActiveSessionView(state: state, controller: controller);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -90,10 +86,7 @@ class _ActiveSessionView extends StatefulWidget {
|
||||
final WorkoutSessionState state;
|
||||
final WorkoutSessionController controller;
|
||||
|
||||
const _ActiveSessionView({
|
||||
required this.state,
|
||||
required this.controller,
|
||||
});
|
||||
const _ActiveSessionView({required this.state, required this.controller});
|
||||
|
||||
@override
|
||||
State<_ActiveSessionView> createState() => _ActiveSessionViewState();
|
||||
@@ -110,8 +103,7 @@ class _ActiveSessionViewState extends State<_ActiveSessionView> {
|
||||
|
||||
final double timeProgress;
|
||||
if (activity != null && activity.duration > 0) {
|
||||
timeProgress =
|
||||
1.0 - (widget.state.timeRemaining / activity.duration);
|
||||
timeProgress = 1.0 - (widget.state.timeRemaining / activity.duration);
|
||||
} else {
|
||||
timeProgress = 0.0;
|
||||
}
|
||||
@@ -128,11 +120,7 @@ class _ActiveSessionViewState extends State<_ActiveSessionView> {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
AppColors.zinc950,
|
||||
accentTint,
|
||||
AppColors.zinc950,
|
||||
],
|
||||
colors: [AppColors.surface, accentTint, AppColors.surface],
|
||||
stops: const [0.0, 0.5, 1.0],
|
||||
),
|
||||
),
|
||||
@@ -149,8 +137,7 @@ class _ActiveSessionViewState extends State<_ActiveSessionView> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (activity != null)
|
||||
ActivityCard(activity: activity),
|
||||
if (activity != null) ActivityCard(activity: activity),
|
||||
const SizedBox(height: UIConstants.spacing32),
|
||||
if (isTimeBased)
|
||||
_CircularTimerDisplay(
|
||||
@@ -249,26 +236,20 @@ class _ActiveSessionViewState extends State<_ActiveSessionView> {
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.zinc800.withValues(alpha: 0.6),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: AppColors.border.withValues(alpha: 0.5),
|
||||
),
|
||||
border: Border.all(color: AppColors.border.withValues(alpha: 0.5)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.timer_outlined,
|
||||
size: 14,
|
||||
color: AppColors.textMuted,
|
||||
),
|
||||
Icon(Icons.timer_outlined, size: 14, color: AppColors.textMuted),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
_formatDuration(widget.state.totalTimeElapsed),
|
||||
style: const TextStyle(
|
||||
style: GoogleFonts.jetBrainsMono(
|
||||
color: AppColors.textSecondary,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFeatures: [FontFeature.tabularFigures()],
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -352,11 +333,10 @@ class _RepsDisplay extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
'$reps',
|
||||
style: TextStyle(
|
||||
style: GoogleFonts.jetBrainsMono(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 72,
|
||||
fontWeight: FontWeight.w200,
|
||||
letterSpacing: -2,
|
||||
fontWeight: FontWeight.w700,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: AppColors.accent.withValues(alpha: 0.3),
|
||||
@@ -462,13 +442,11 @@ class _CircularTimerDisplay extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
_formatTime(timeRemaining),
|
||||
style: TextStyle(
|
||||
style: GoogleFonts.jetBrainsMono(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 64,
|
||||
fontWeight: FontWeight.w200,
|
||||
letterSpacing: -1,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
fontFamily: 'monospace',
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: ringColor.withValues(alpha: 0.3),
|
||||
@@ -525,10 +503,7 @@ class TimerRingPainter extends CustomPainter {
|
||||
final double progress;
|
||||
final Color ringColor;
|
||||
|
||||
TimerRingPainter({
|
||||
required this.progress,
|
||||
required this.ringColor,
|
||||
});
|
||||
TimerRingPainter({required this.progress, required this.ringColor});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
@@ -551,10 +526,10 @@ class TimerRingPainter extends CustomPainter {
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth * 2
|
||||
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 16);
|
||||
|
||||
|
||||
final sweepAngle = 2 * math.pi * progress;
|
||||
final startAngle = -math.pi / 2;
|
||||
|
||||
|
||||
canvas.drawArc(
|
||||
Rect.fromCircle(center: center, radius: radius),
|
||||
startAngle,
|
||||
@@ -576,7 +551,9 @@ class TimerRingPainter extends CustomPainter {
|
||||
);
|
||||
|
||||
final progressPaint = Paint()
|
||||
..shader = gradient.createShader(Rect.fromCircle(center: center, radius: radius))
|
||||
..shader = gradient.createShader(
|
||||
Rect.fromCircle(center: center, radius: radius),
|
||||
)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeCap = StrokeCap.round
|
||||
..strokeWidth = strokeWidth;
|
||||
@@ -593,7 +570,7 @@ class TimerRingPainter extends CustomPainter {
|
||||
@override
|
||||
bool shouldRepaint(TimerRingPainter oldDelegate) {
|
||||
return oldDelegate.progress != progress ||
|
||||
oldDelegate.ringColor != ringColor;
|
||||
oldDelegate.ringColor != ringColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,10 +578,7 @@ class _UpNextPill extends StatelessWidget {
|
||||
final String nextActivityName;
|
||||
final bool isNextRest;
|
||||
|
||||
const _UpNextPill({
|
||||
required this.nextActivityName,
|
||||
required this.isNextRest,
|
||||
});
|
||||
const _UpNextPill({required this.nextActivityName, required this.isNextRest});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -693,9 +667,7 @@ class _ActivitiesListPanel extends StatelessWidget {
|
||||
width: 320,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.surfaceContainer,
|
||||
border: Border(
|
||||
left: BorderSide(color: AppColors.border),
|
||||
),
|
||||
border: Border(left: BorderSide(color: AppColors.border)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -763,8 +735,8 @@ class _ActivitiesListPanel extends StatelessWidget {
|
||||
color: isCurrent
|
||||
? AppColors.accent
|
||||
: isRest
|
||||
? AppColors.info
|
||||
: AppColors.zinc600,
|
||||
? AppColors.info
|
||||
: AppColors.zinc600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UIConstants.spacing12),
|
||||
@@ -785,7 +757,8 @@ class _ActivitiesListPanel extends StatelessWidget {
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
if (!isRest && activity.setIndex != null)
|
||||
if (!isRest &&
|
||||
activity.setIndex != null)
|
||||
Text(
|
||||
'Set ${activity.setIndex}/${activity.totalSets} · ${activity.sectionName ?? ''}',
|
||||
style: const TextStyle(
|
||||
@@ -888,22 +861,19 @@ class _CompletionScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing24),
|
||||
const Text(
|
||||
'Workout Complete',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
'WORKOUT COMPLETE',
|
||||
style: GoogleFonts.chakraPetch(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.5,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 1,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing8),
|
||||
Text(
|
||||
'Great job! You crushed it.',
|
||||
style: TextStyle(
|
||||
color: AppColors.textSecondary,
|
||||
fontSize: 15,
|
||||
),
|
||||
style: TextStyle(color: AppColors.textSecondary, fontSize: 15),
|
||||
),
|
||||
const SizedBox(height: UIConstants.spacing32),
|
||||
Container(
|
||||
@@ -932,12 +902,11 @@ class _CompletionScreen extends StatelessWidget {
|
||||
const SizedBox(height: UIConstants.spacing4),
|
||||
Text(
|
||||
_formatDuration(totalTimeElapsed),
|
||||
style: TextStyle(
|
||||
style: GoogleFonts.jetBrainsMono(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w300,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -21,8 +21,8 @@ class WorkoutSessionState with _$WorkoutSessionState {
|
||||
|
||||
WorkoutActivityEntity? get nextActivity =>
|
||||
currentIndex + 1 < activities.length
|
||||
? activities[currentIndex + 1]
|
||||
: null;
|
||||
? activities[currentIndex + 1]
|
||||
: null;
|
||||
|
||||
double get progress =>
|
||||
activities.isEmpty ? 0.0 : currentIndex / activities.length;
|
||||
|
||||
Reference in New Issue
Block a user