Spaces:
Running
Running
| import 'package:flutter/cupertino.dart'; | |
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | |
| import 'package:go_router/go_router.dart'; | |
| import 'package:intl/intl.dart'; | |
| import 'package:trainlog/src/data/models/workout.dart'; | |
| import 'package:trainlog/src/presentation/providers/workout_providers.dart'; | |
| class WorkoutsScreen extends ConsumerWidget { | |
| const WorkoutsScreen({super.key}); | |
| Widget build(BuildContext context, WidgetRef ref) { | |
| final workoutsAsync = ref.watch(workoutsProvider); | |
| return CupertinoPageScaffold( | |
| navigationBar: CupertinoNavigationBar( | |
| middle: const Text('Historia treningów'), | |
| trailing: CupertinoButton( | |
| padding: EdgeInsets.zero, | |
| child: const Icon(CupertinoIcons.add), | |
| onPressed: () => context.go('/workout/new'), | |
| ), | |
| ), | |
| child: workoutsAsync.when( | |
| data: (workouts) => workouts.isEmpty | |
| ? const _EmptyState() | |
| : ListView.builder( | |
| padding: const EdgeInsets.all(16), | |
| itemCount: workouts.length, | |
| itemBuilder: (context, index) { | |
| final workout = workouts[index]; | |
| return _WorkoutListItem( | |
| workout: workout, | |
| onTap: () => context.go('/workout/edit/${workout.id}'), | |
| onDuplicate: () => _duplicateWorkout(context, ref, workout.id), | |
| ); | |
| }, | |
| ), | |
| loading: () => const Center(child: CupertinoActivityIndicator()), | |
| error: (err, _) => Center(child: Text('Błąd: $err')), | |
| ), | |
| ); | |
| } | |
| Future<void> _duplicateWorkout(BuildContext context, WidgetRef ref, int id) async { | |
| final repo = ref.read(workoutRepositoryProvider); | |
| try { | |
| await repo.duplicateWorkout(id); | |
| if (context.mounted) { | |
| showCupertinoDialog( | |
| context: context, | |
| builder: (_) => CupertinoAlertDialog( | |
| title: const Text('Sukces'), | |
| content: const Text('Trening został zduplikowany'), | |
| actions: [ | |
| CupertinoDialogAction( | |
| child: const Text('OK'), | |
| onPressed: () => Navigator.pop(context), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } catch (e) { | |
| if (context.mounted) { | |
| showCupertinoDialog( | |
| context: context, | |
| builder: (_) => CupertinoAlertDialog( | |
| title: const Text('Błąd'), | |
| content: Text(e.toString()), | |
| actions: [ | |
| CupertinoDialogAction( | |
| child: const Text('OK'), | |
| onPressed: () => Navigator.pop(context), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| class _WorkoutListItem extends StatelessWidget { | |
| final Workout workout; | |
| final VoidCallback onTap; | |
| final VoidCallback onDuplicate; | |
| const _WorkoutListItem({ | |
| required this.workout, | |
| required this.onTap, | |
| required this.onDuplicate, | |
| }); | |
| Widget build(BuildContext context) { | |
| return Dismissible( | |
| key: Key(workout.id.toString()), | |
| direction: DismissDirection.endToStart, | |
| background: Container( | |
| alignment: Alignment.centerRight, | |
| padding: const EdgeInsets.only(right: 20), | |
| color: CupertinoColors.destructiveRed, | |
| child: const Icon(CupertinoIcons.delete, color: CupertinoColors.white), | |
| ), | |
| onDismissed: (_) => onDuplicate(), | |
| child: Container( | |
| margin: const EdgeInsets.only(bottom: 8), | |
| child: CupertinoButton( | |
| padding: EdgeInsets.zero, | |
| onPressed: onTap, | |
| child: Container( | |
| padding: const EdgeInsets.all(16), | |
| decoration: BoxDecoration( | |
| color: CupertinoColors.systemBackground.resolveFrom(context), | |
| borderRadius: BorderRadius.circular(10), | |
| ), | |
| child: Row( | |
| children: [ | |
| Container( | |
| width: 48, | |
| height: 48, | |
| decoration: BoxDecoration( | |
| color: CupertinoColors.systemGrey6.resolveFrom(context), | |
| borderRadius: BorderRadius.circular(8), | |
| ), | |
| child: Center( | |
| child: Text( | |
| workout.type.icon, | |
| style: const TextStyle(fontSize: 20), | |
| ), | |
| ), | |
| ), | |
| const SizedBox(width: 12), | |
| Expanded( | |
| child: Column( | |
| crossAxisAlignment: CrossAxisAlignment.start, | |
| children: [ | |
| Text( | |
| DateFormat('d MMM yyyy, HH:mm', 'pl_PL').format(workout.date), | |
| style: const TextStyle( | |
| fontSize: 15, | |
| fontWeight: FontWeight.w600, | |
| ), | |
| ), | |
| const SizedBox(height: 2), | |
| Text( | |
| workout.type.displayName, | |
| style: TextStyle( | |
| fontSize: 13, | |
| color: CupertinoColors.secondaryLabel.resolveFrom(context), | |
| ), | |
| ), | |
| if (workout.note != null && workout.note!.isNotEmpty) | |
| Text( | |
| workout.note!, | |
| maxLines: 1, | |
| overflow: TextOverflow.ellipsis, | |
| style: TextStyle( | |
| fontSize: 12, | |
| color: CupertinoColors.tertiaryLabel.resolveFrom(context), | |
| ), | |
| ), | |
| ], | |
| ), | |
| ), | |
| CupertinoButton( | |
| padding: EdgeInsets.zero, | |
| child: const Icon( | |
| CupertinoIcons.doc_on_doc, | |
| size: 20, | |
| color: CupertinoColors.activeOrange, | |
| ), | |
| onPressed: () { | |
| showCupertinoModalPopup( | |
| context: context, | |
| builder: (context) => CupertinoActionSheet( | |
| title: const Text('Opcje'), | |
| actions: [ | |
| CupertinoActionSheetAction( | |
| onPressed: () { | |
| Navigator.pop(context); | |
| onDuplicate(); | |
| }, | |
| child: const Text('Duplikuj trening'), | |
| ), | |
| ], | |
| cancelButton: CupertinoActionSheetAction( | |
| onPressed: () => Navigator.pop(context), | |
| isDefaultAction: true, | |
| child: const Text('Anuluj'), | |
| ), | |
| ), | |
| ); | |
| }, | |
| ), | |
| ], | |
| ), | |
| ), | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| class _EmptyState extends StatelessWidget { | |
| const _EmptyState(); | |
| Widget build(BuildContext context) { | |
| return Center( | |
| child: Column( | |
| mainAxisAlignment: MainAxisAlignment.center, | |
| children: [ | |
| Icon( | |
| CupertinoIcons.list_bullet, | |
| size: 64, | |
| color: CupertinoColors.systemGrey.resolveFrom(context), | |
| ), | |
| const SizedBox(height: 16), | |
| Text( | |
| 'Brak treningów', | |
| style: TextStyle( | |
| fontSize: 20, | |
| fontWeight: FontWeight.w600, | |
| color: CupertinoColors.secondaryLabel.resolveFrom(context), | |
| ), | |
| ), | |
| const SizedBox(height: 8), | |
| Text( | |
| 'Naciśnij + aby dodać pierwszy trening', | |
| style: TextStyle( | |
| color: CupertinoColors.tertiaryLabel.resolveFrom(context), | |
| ), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } |