import 'package:flutter/material.dart'; import '../../app_theme.dart' show AppTheme; import '../../model/requests/create_fixed_time_alarm_request.dart'; import '../../service/tools.dart'; import '../../service/validators.dart'; class FixedTimeAlarmForm extends StatefulWidget { const FixedTimeAlarmForm({ super.key, required this.request, required this.onChanged, }); final CreateFixedTimeAlarmRequest request; final ValueChanged onChanged; @override State createState() => _FixedTimeAlarmFormState(); } class _FixedTimeAlarmFormState extends State { final dueDateController = TextEditingController(); final dueTimeController = TextEditingController(); final dueDateFocusNode = FocusNode(); final dueTimeFocusNode = FocusNode(); bool isDueTimeEnabled = false; @override void didChangeDependencies() { if (widget.request.triggerAt != null) { dueDateController.text = getIsoDateString(widget.request.triggerAt!); dueTimeController.text = TimeOfDay.fromDateTime( widget.request.triggerAt!, ).format(context); isDueTimeEnabled = true; } dueDateController.addListener(onDueDateChanged); dueTimeController.addListener(onDueTimeChanged); super.didChangeDependencies(); } @override Widget build(BuildContext context) { return Flex( direction: Axis.horizontal, children: [ Flexible( flex: 3, child: TextFormField( focusNode: dueDateFocusNode, controller: dueDateController, onFieldSubmitted: (_) { isDueTimeEnabled ? dueDateFocusNode.nextFocus() : null; }, decoration: InputDecoration( label: Text('Due Date'), suffixIcon: IconButton( onPressed: () async { final result = await onOpenCalendarPickerPressed(); if (result != null) { final dateString = getIsoDateString(result); dueDateController.text = dateString; maybeEnableDueTime(dateString); dueTimeFocusNode.requestFocus(); } }, icon: Icon(Icons.calendar_month), ), ), validator: dateTimeValidator, keyboardType: TextInputType.datetime, textInputAction: TextInputAction.next, ), ), Padding( padding: EdgeInsetsGeometry.only(left: AppTheme.formColumnSpacing), ), Flexible( flex: 2, child: TextFormField( focusNode: dueTimeFocusNode, onFieldSubmitted: (_) => dueTimeFocusNode.nextFocus(), controller: dueTimeController, enabled: isDueTimeEnabled, onChanged: (value) {}, decoration: InputDecoration( label: Text('Due Time'), suffixIcon: IconButton( onPressed: () async { final result = await onOpenTimePickerPressed(); if (result != null && context.mounted) { dueTimeController.text = result.format(context); } }, icon: Icon(Icons.schedule), ), ), validator: timeValidator, keyboardType: TextInputType.text, textInputAction: TextInputAction.next, ), ), ], ); } Future onOpenCalendarPickerPressed() { return showDialog( context: context, builder: (context) => DatePickerDialog( firstDate: DateTime(DateTime.now().year - 100), lastDate: DateTime(DateTime.now().year + 100), initialDate: DateTime.tryParse(dueDateController.text) ?? DateTime.now(), ), ); } Future onOpenTimePickerPressed() { return showDialog( context: context, builder: (context) => TimePickerDialog( initialTime: TimeOfDay.fromDateTime( widget.request.triggerAt ?? DateTime.now(), ), ), ); } void maybeEnableDueTime(String value) { if (value.isNotEmpty && dateTimeValidator(value) == null) { setState(() { isDueTimeEnabled = true; }); } else if (isDueTimeEnabled) { setState(() { isDueTimeEnabled = false; }); } } void onDueDateChanged() { final String value = dueDateController.text; maybeEnableDueTime(value); if (dateTimeValidator(value) == null) { final dateTime = DateTime.tryParse( '$value ${dueTimeController.text}'.trim(), ); if (dateTime != null) { widget.onChanged(CreateFixedTimeAlarmRequest(triggerAt: dateTime)); } } } void onDueTimeChanged() { final String value = dueTimeController.text; if (timeValidator(value) == null) { final dateTime = DateTime.tryParse( '${dueDateController.text} $value'.trim(), ); if (dateTime != null) { widget.onChanged(CreateFixedTimeAlarmRequest(triggerAt: dateTime)); } } } }