diff --git a/lib/widgets/routines/forms/routine.dart b/lib/widgets/routines/forms/routine.dart index d8bb5bd1..fab2bee8 100644 --- a/lib/widgets/routines/forms/routine.dart +++ b/lib/widgets/routines/forms/routine.dart @@ -10,6 +10,8 @@ import 'package:wger/providers/routines.dart'; import 'package:wger/screens/routine_edit_screen.dart'; import 'package:wger/widgets/core/progress_indicator.dart'; +/// A form widget for creating and editing workout routines. +/// Handles validation and user input for routine details, including start and end dates. class RoutineForm extends StatefulWidget { final Routine _routine; final bool useListView; @@ -27,9 +29,11 @@ class _RoutineFormState extends State { bool isSaving = false; late bool fitInWeek; late DateTime startDate; - late DateTime endDate; + DateTime? endDate; final workoutNameController = TextEditingController(); final workoutDescriptionController = TextEditingController(); + final startDateController = TextEditingController(); + final endDateController = TextEditingController(); @override void initState() { @@ -38,13 +42,20 @@ class _RoutineFormState extends State { workoutNameController.text = widget._routine.name; workoutDescriptionController.text = widget._routine.description; startDate = widget._routine.start; - endDate = widget._routine.end; + endDate = widget._routine.end == widget._routine.start ? null : widget._routine.end; + WidgetsBinding.instance.addPostFrameCallback((_) { + final locale = Localizations.localeOf(context).languageCode; + startDateController.text = DateFormat.yMd(locale).format(startDate); + endDateController.text = endDate != null ? DateFormat.yMd(locale).format(endDate!) : ''; + }); } @override void dispose() { workoutNameController.dispose(); workoutDescriptionController.dispose(); + startDateController.dispose(); + endDateController.dispose(); super.dispose(); } @@ -94,17 +105,18 @@ class _RoutineFormState extends State { ), TextFormField( key: const Key('field-start-date'), - // Stop keyboard from appearing readOnly: true, validator: (value) { - if (endDate.isBefore(startDate)) { - return 'End date must be after start date'; - } - if (endDate.difference(startDate).inDays < Routine.MIN_DURATION * 7) { - return 'Duration of the routine must be more than ${Routine.MIN_DURATION} weeks'; - } - if (endDate.difference(startDate).inDays > Routine.MAX_DURATION * 7) { - return 'Duration of the routine must be less than ${Routine.MAX_DURATION} weeks'; + if (endDate != null) { + if (endDate!.isBefore(startDate)) { + return 'End date must be after start date'; + } + if (endDate!.difference(startDate).inDays < Routine.MIN_DURATION * 7) { + return 'Duration of the routine must be more than {Routine.MIN_DURATION} weeks'; + } + if (endDate!.difference(startDate).inDays > Routine.MAX_DURATION * 7) { + return 'Duration of the routine must be less than {Routine.MAX_DURATION} weeks'; + } } return null; }, @@ -116,11 +128,7 @@ class _RoutineFormState extends State { ), ), enableInteractiveSelection: false, - controller: TextEditingController( - text: DateFormat.yMd( - Localizations.localeOf(context).languageCode, - ).format(startDate), - ), + controller: startDateController, onTap: () async { final picked = await showDatePicker( context: context, @@ -137,14 +145,18 @@ class _RoutineFormState extends State { if (mounted) { setState(() { startDate = picked; + final locale = Localizations.localeOf(context).languageCode; + startDateController.text = DateFormat.yMd(locale).format(startDate); }); } }, ), TextFormField( key: const Key('field-end-date'), - // Stop keyboard from appearing readOnly: true, + /// Opens a date picker for selecting the routine's end date. + /// Ensures the end date is always after the start date and allows selection up to 10 years in the future. + /// The initial date shown is the current end date if valid, otherwise the minimum allowed end date. decoration: const InputDecoration( labelText: 'End date', suffixIcon: Icon( @@ -153,17 +165,19 @@ class _RoutineFormState extends State { ), ), enableInteractiveSelection: false, - controller: TextEditingController( - text: DateFormat.yMd( - Localizations.localeOf(context).languageCode, - ).format(endDate), - ), + controller: endDateController, onTap: () async { + final now = DateTime.now(); + final firstEndDate = startDate.add(const Duration(days: 1)); + DateTime initial = firstEndDate; + if (endDate != null && endDate!.isAfter(firstEndDate)) { + initial = endDate!; + } final picked = await showDatePicker( context: context, - initialDate: endDate, - firstDate: DateTime(DateTime.now().year - 10), - lastDate: DateTime.now(), + initialDate: initial, + firstDate: firstEndDate, + lastDate: now.add(const Duration(days: 365 * 10)), ); if (picked == null) { @@ -174,6 +188,8 @@ class _RoutineFormState extends State { if (mounted) { setState(() { endDate = picked; + final locale = Localizations.localeOf(context).languageCode; + endDateController.text = DateFormat.yMd(locale).format(endDate!); }); } },