diff --git a/lib/pages/task_edit_page.dart b/lib/pages/task_edit_page.dart index 865cf32..5e35db8 100644 --- a/lib/pages/task_edit_page.dart +++ b/lib/pages/task_edit_page.dart @@ -17,17 +17,20 @@ class TaskEditPage extends StatefulWidget { class _TaskEditPageState extends State { Task? task; - bool isInitialized = true; + bool isInitialized = false; final titleController = TextEditingController(); final descriptionController = TextEditingController(); final categoryController = TextEditingController(); final urlController = TextEditingController(); final dueDateController = TextEditingController(); final dueTimeController = TextEditingController(); - late String pageTitle; + String pageTitle = 'Create Task'; final dueDateFocusNode = FocusNode(); final dueTimeFocusNode = FocusNode(); final categoryFocusNode = FocusNode(); + final formKey = GlobalKey(debugLabel: 'taskEditFormKey'); + bool didFormChange = false; + bool isDueTimeEnabled = false; @override void didChangeDependencies() { @@ -38,15 +41,16 @@ class _TaskEditPageState extends State { descriptionController.text = task!.description; categoryController.text = task!.category; urlController.text = task!.url; - dueDateController.text = task!.due != null - ? getIsoDateString(task!.due!) - : ''; - dueTimeController.text = task!.due != null - ? TimeOfDay.fromDateTime(task!.due!).format(context) - : ''; + if (task!.due != null) { + dueDateController.text = getIsoDateString(task!.due!); + dueTimeController.text = TimeOfDay.fromDateTime( + task!.due!, + ).format(context); + isDueTimeEnabled = true; + } isInitialized = true; + pageTitle = task!.title; } - pageTitle = task?.title ?? 'CreateTask'; } @override @@ -66,7 +70,11 @@ class _TaskEditPageState extends State { ], ), body: Form( + canPop: !didFormChange, + onChanged: () => didFormChange = true, + onPopInvokedWithResult: onPopInvoked, autovalidateMode: AutovalidateMode.onUnfocus, + key: formKey, child: Padding( padding: EdgeInsets.symmetric( horizontal: MediaQuery.of(context).size.width * 0.05, @@ -91,14 +99,22 @@ class _TaskEditPageState extends State { TextFormField( focusNode: dueDateFocusNode, controller: dueDateController, - onFieldSubmitted: (_) => dueDateFocusNode.nextFocus(), + onChanged: maybeEnableDueTime, + onFieldSubmitted: (_) { + isDueTimeEnabled + ? dueDateFocusNode.nextFocus() + : categoryFocusNode.requestFocus(); + }, decoration: InputDecoration( label: Text('Due Date'), suffix: IconButton( onPressed: () async { final result = await onOpenCalendarPickerPressed(); if (result != null) { - dueDateController.text = getIsoDateString(result); + final dateString = getIsoDateString(result); + dueDateController.text = dateString; + maybeEnableDueTime(dateString); + formKey.currentState?.validate(); dueTimeFocusNode.requestFocus(); } }, @@ -113,9 +129,7 @@ class _TaskEditPageState extends State { focusNode: dueTimeFocusNode, onFieldSubmitted: (_) => dueTimeFocusNode.nextFocus(), controller: dueTimeController, - enabled: DateTime.tryParse(dueDateController.text) != null - ? true - : false, + enabled: isDueTimeEnabled, decoration: InputDecoration( label: Text('Due Time'), suffix: IconButton( @@ -172,8 +186,9 @@ class _TaskEditPageState extends State { Future onOpenTimePickerPressed() { return showDialog( context: context, - builder: (context) => - TimePickerDialog(initialTime: TimeOfDay(hour: 0, minute: 0)), + builder: (context) => TimePickerDialog( + initialTime: TimeOfDay.fromDateTime(task?.due ?? DateTime.now()), + ), ); } @@ -183,7 +198,7 @@ class _TaskEditPageState extends State { title: titleController.text, description: descriptionController.text, start: null, - due: DateTime.tryParse(dueDateController.text), + due: null, isCompleted: false, category: categoryController.text, subtasks: [], @@ -193,4 +208,46 @@ class _TaskEditPageState extends State { ), ); } + + void onPopInvoked(bool didPop, Object? result) { + if (!didPop) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Unsaved Changes'), + content: Text( + 'Are you sure you want to cancel editing? Unsaved changes will be lost.', + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('No'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text('Yes'), + ), + ], + ), + ).then((result) { + if (result != null && result && context.mounted) { + Navigator.of(context).pop(); + } + }); + } + } + + void maybeEnableDueTime(String value) { + if (value.isNotEmpty && + dateTimeValidator(value) == null && + !isDueTimeEnabled) { + setState(() { + isDueTimeEnabled = true; + }); + } else if (isDueTimeEnabled) { + setState(() { + isDueTimeEnabled = false; + }); + } + } }