Skip to content

feat: Unsaved changes indicator with review dialog on Edit Task screen#524

Open
mulikruchi07 wants to merge 4 commits into
CCExtractor:mainfrom
mulikruchi07:feature/unsaved-changes-indicator
Open

feat: Unsaved changes indicator with review dialog on Edit Task screen#524
mulikruchi07 wants to merge 4 commits into
CCExtractor:mainfrom
mulikruchi07:feature/unsaved-changes-indicator

Conversation

@mulikruchi07
Copy link
Copy Markdown
Contributor

@mulikruchi07 mulikruchi07 commented Dec 18, 2025

Description

This PR adds a visual indicator for unsaved changes on the Edit Task screen.
When a user modifies any task attribute, a Save button appears at the bottom-right.
Clicking Save opens a review dialog that clearly shows previous vs updated values,
allowing the user to either submit or cancel the changes.
The Save indicator automatically disappears if all changes are reverted back to their original values, ensuring accurate change tracking and preventing empty review dialogs.

Fixes #523

Screenshots

enh_after_524.mp4

Checklist

  • Tests have been added or updated to cover the changes
  • Documentation has been updated to reflect the changes
  • Code follows the established coding style guidelines
  • All tests are passing

Summary by CodeRabbit

  • Improvements

    • Edit state now accurately reflects pending changes.
    • Save button only displays when modifications exist.
    • Added confirmation dialog to review changes before saving.
  • Bug Fixes

    • Enhanced priority selection cycling and value normalization for consistency.

Review Change Stack

Copilot AI review requested due to automatic review settings December 18, 2025 14:05
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements an unsaved changes indicator on the Edit Task screen. When a user modifies any task attribute, a floating action button with a save icon appears in the bottom-right corner. Clicking it opens a review dialog showing the changes (old vs new values), where the user can either submit or cancel. The save button automatically disappears when all changes are reverted to their original values, ensuring accurate change tracking.

Key changes include:

  • Enhanced priority widget to normalize null/empty values to display as 'X' and cycle through priority levels (X → H → M → L → X)
  • Refactored the save flow by extracting the review dialog into a separate method with improved user feedback via SnackBar
  • Updated change tracking logic to reflect real changes only by checking modify.changes.isNotEmpty

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
lib/app/modules/detailRoute/views/priority_widget.dart Adds null/empty value normalization to 'X' for display, implements priority cycling logic with editable state handling
lib/app/modules/detailRoute/views/detail_route_view.dart Refactors floating action button to use observable pattern, extracts review dialog to separate method, adds SnackBar feedback after save
lib/app/modules/detailRoute/controllers/detail_route_controller.dart Updates onEdit flag logic to track real changes by checking modify.changes.isNotEmpty after setAttribute and saveChanges operations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/app/modules/detailRoute/views/priority_widget.dart Outdated
Comment on lines +224 to +234
onPressed: () {
Get.back();
controller.saveChanges();

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(sentences.taskUpdated),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The navigation flow is broken. When the dialog is closed with Get.back() at line 225, and then controller.saveChanges() is called which contains another Get.back() at line 84 of detail_route_controller.dart, this will navigate back twice. The second Get.back() in saveChanges() will close the detail route view itself instead of just the dialog. Additionally, the SnackBar at lines 228-234 will be displayed after the view has already been popped, which may not work correctly. Consider removing the Get.back() call from the controller's saveChanges() method or restructuring this flow.

Copilot uses AI. Check for mistakes.
Comment on lines +227 to +234

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(sentences.taskUpdated),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate SnackBar notifications will be shown to the user. The controller.saveChanges() method already displays a SnackBar (lines 76-81 in detail_route_controller.dart), and this code adds another SnackBar at lines 228-234. This will result in two "Task Updated" messages appearing to the user, creating a confusing experience. Consider either removing the SnackBar from the controller's saveChanges() method or removing it from this location.

Suggested change
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(sentences.taskUpdated),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);

Copilot uses AI. Check for mistakes.
modify.set(name, newValue);
onEdit.value = true;

// onEdit must reflect REAL changes only
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should be properly formatted with consistent spacing. Comments should start with a space after the double-slash to follow Dart style conventions.

Suggested change
// onEdit must reflect REAL changes only
// onEdit must reflect REAL changes only

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

The PR implements visual tracking of unsaved changes in the edit task screen. The controller now synchronizes the onEdit flag to reflect whether pending modifications exist. The view conditionally displays a Save button and opens a review dialog showing all pending changes before submission. The priority widget is refactored to normalize priority values and support cycling through priority states.

Changes

Edit task with unsaved changes tracking and review dialog

Layer / File(s) Summary
Edit state tracking in controller
lib/app/modules/detailRoute/controllers/detail_route_controller.dart
onEdit.value is synchronized to modify.changes.isNotEmpty in both setAttribute and saveChanges, ensuring the flag reflects actual pending modifications rather than being set unconditionally.
Review dialog and conditional save button
lib/app/modules/detailRoute/views/detail_route_view.dart
Attribute list rendering is refactored to Obx-wrapped ListView. The save FloatingActionButton is shown only when controller.onEdit.value is true. A dedicated _showReviewChangesDialog helper displays a scrollable dialog of pending changes; on submit it saves and shows a snackbar, on cancel it closes without saving.
Priority widget value normalization and interaction
lib/app/modules/detailRoute/views/priority_widget.dart
Priority value is normalized to 'X' (empty), 'H', 'M', or 'L' for consistent rendering and interaction. Tap handler cycles through priorities when editable and disables taps when read-only.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A widget learns to track its changes true,
When edits flow, the Save button shows its hue.
Priority cycles, values dance with care,
Review dialogs whisper: "Here's what's fair!"
Changes glisten, unsaved no more—
Just peek, then save, and onward soar! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main feature: adding an unsaved changes indicator with a review dialog on the Edit Task screen.
Description check ✅ Passed The description covers the main feature summary, references the linked issue #523, and includes screenshots. However, testing, documentation, and passing-tests items remain unchecked.
Linked Issues check ✅ Passed All requirements from issue #523 are implemented: Save button appears/disappears based on unsaved changes, Review Changes dialog shows old vs new values, and Cancel/Submit options work correctly.
Out of Scope Changes check ✅ Passed All changes directly address issue #523 requirements: DetailRouteController tracks modification state, DetailRouteView implements visibility and dialog, PriorityWidget normalizes priority values.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/app/modules/detailRoute/views/detail_route_view.dart`:
- Around line 224-235: The dialog handler is showing a duplicate snackbar and
performing navigation that controller.saveChanges() already handles; remove the
explicit Get.back() call and the ScaffoldMessenger.of(context).showSnackBar(...)
block (including the SnackBar creation and sentences.taskUpdated usage) and
instead just invoke controller.saveChanges() so feedback and navigation come
solely from controller.saveChanges().

In `@lib/app/modules/detailRoute/views/priority_widget.dart`:
- Around line 31-33: The priority string is not canonicalized so values like "
low", "l", or "low" won't match the switch/case at the tap handler; update the
priority handling by normalizing the value right after it's computed (use trim()
and toUpperCase(), e.g., set priority = (value == null || value == '') ? 'X' :
value.toString().trim().toUpperCase()) and also add a safe default branch in the
tap-handling switch/mapping (the code around the onTap handler at lines ~70-83)
so any unmatched value falls back to a known priority or no-op handler,
preventing silent taps.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 016ceabe-fe3c-4352-8ac8-fe724a848e6f

📥 Commits

Reviewing files that changed from the base of the PR and between d802b00 and 106bf08.

📒 Files selected for processing (3)
  • lib/app/modules/detailRoute/controllers/detail_route_controller.dart
  • lib/app/modules/detailRoute/views/detail_route_view.dart
  • lib/app/modules/detailRoute/views/priority_widget.dart

Comment on lines +224 to +235
onPressed: () {
Get.back();
controller.saveChanges();

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(sentences.taskUpdated),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove duplicate success notification from the dialog submit path.

controller.saveChanges() already handles success feedback and navigation. On Line 228, showing another ScaffoldMessenger snackbar after popping can duplicate messages and rely on a dialog context that may no longer be valid.

Proposed fix
             TextButton(
               onPressed: () {
                 Get.back();
                 controller.saveChanges();
-
-                ScaffoldMessenger.of(context).showSnackBar(
-                  SnackBar(
-                    content: Text(sentences.taskUpdated),
-                    behavior: SnackBarBehavior.floating,
-                    duration: const Duration(seconds: 2),
-                  ),
-                );
               },
               child: Text(
                 sentences.submit,
                 style: const TextStyle(color: Colors.white),
               ),
             ),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onPressed: () {
Get.back();
controller.saveChanges();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(sentences.taskUpdated),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);
},
onPressed: () {
Get.back();
controller.saveChanges();
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/modules/detailRoute/views/detail_route_view.dart` around lines 224 -
235, The dialog handler is showing a duplicate snackbar and performing
navigation that controller.saveChanges() already handles; remove the explicit
Get.back() call and the ScaffoldMessenger.of(context).showSnackBar(...) block
(including the SnackBar creation and sentences.taskUpdated usage) and instead
just invoke controller.saveChanges() so feedback and navigation come solely from
controller.saveChanges().

Comment on lines +31 to +33
final String priority =
(value == null || value == '') ? 'X' : value.toString();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle non-canonical priority values to avoid silent no-op taps.

At Line 31-32 and Line 70-83, unexpected values (e.g., lowercase or spaced strings) won’t match any case, so tapping does nothing even when editable. Canonicalize before switching or add a default fallback.

Proposed fix
-    final String priority =
-        (value == null || value == '') ? 'X' : value.toString();
+    final String priority = (() {
+      final raw = value?.toString().trim().toUpperCase();
+      return (raw == null || raw.isEmpty) ? 'X' : raw;
+    })();

...
                 switch (priority) {
                   case 'X':
                     callback('H');
                     break;
                   case 'H':
                     callback('M');
                     break;
                   case 'M':
                     callback('L');
                     break;
                   case 'L':
                     callback(null);
                     break;
+                  default:
+                    callback('H');
+                    break;
                 }

Also applies to: 70-83

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/app/modules/detailRoute/views/priority_widget.dart` around lines 31 - 33,
The priority string is not canonicalized so values like " low", "l", or "low"
won't match the switch/case at the tap handler; update the priority handling by
normalizing the value right after it's computed (use trim() and toUpperCase(),
e.g., set priority = (value == null || value == '') ? 'X' :
value.toString().trim().toUpperCase()) and also add a safe default branch in the
tap-handling switch/mapping (the code around the onTap handler at lines ~70-83)
so any unmatched value falls back to a known priority or no-op handler,
preventing silent taps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add visual indicator and review dialog for unsaved changes in Edit Task screen

3 participants