Skip to content

fix: address use_build_context_synchronously warnings in UI components#624

Open
itsofficially-sreyash wants to merge 2 commits intoCCExtractor:mainfrom
itsofficially-sreyash:main
Open

fix: address use_build_context_synchronously warnings in UI components#624
itsofficially-sreyash wants to merge 2 commits intoCCExtractor:mainfrom
itsofficially-sreyash:main

Conversation

@itsofficially-sreyash
Copy link

@itsofficially-sreyash itsofficially-sreyash commented Mar 13, 2026

Overview

Hey team! While performing a code audit and running flutter analyze, I identified several instances where BuildContext was being accessed across asynchronous
gaps.

In Flutter, accessing a BuildContext after an await call without checking if the widget is still "mounted" (active on the screen) is a common source of
unexpected crashes and "silent" errors. If a user navigates away from a page while a background task (like saving a task or fetching a profile) is running, the
app might try to show a SnackBar or pop a dialog using an invalid context.

This PR adds surgical stability checks (if (!context.mounted) return;) across 9 files to ensure the app remains rock-solid during async operations.

What’s Changed?

  1. Safer User Feedback (SnackBars & Dialogs)
    I’ve updated the "Add Task" bottom sheets and "Profile" views. Now, if a user hits "Save" and then quickly closes the sheet or switches tabs before the database/sync completes, the app will safely cancel the subsequent UI updates instead of trying to interact with a dead context.
  • Impacted: add_task_bottom_sheet_new.dart, deleteprofiledialog.dart, profile_view.dart.
  1. Guarded Navigation logic
    I refactored the navigation callbacks in the Floating Action Button and the Manage Credentials view. This ensures that the app won't try to trigger sync processes or navigate "Back" if the underlying widget has already been disposed.
  • Impacted: home_page_floating_action_button.dart, manage_task_champion_creds_view.dart.
  1. Stabilized In-App Tours & Pickers
    The "Tutorial Coach Mark" logic in the Home, Profile, and Reports modules was triggering after a 500ms delay. I added guards here to prevent the tour from attempting to start if the user has already navigated away from the page during that half-second window.
  • Impacted: profile_controller.dart, reports_controller.dart, detail_route_controller.dart, date_picker_input.dart.

Technical Details

  • The Fix: Every await operation that is followed by a UI-related call (like ScaffoldMessenger, Navigator, or Get.back) now includes an explicit check:

1 await someAsyncOperation();
2 if (!context.mounted) return; // Guard against unmounted context
3 showSnackBar();

  • Verification: Verified by running flutter analyze, which confirmed that all use_build_context_synchronously warnings in these files have been resolved.

Happy to discuss these changes or jump into the next set of linting fixes!

Summary by CodeRabbit

  • Bug Fixes

    • Improved stability by preventing UI actions from running on screens that have been dismissed while asynchronous operations complete (fixes potential crashes across tasks, profile, credentials, and reports flows).
  • Chores

    • Project dependency updated to support internal package behavior.

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b15b569d-771b-494f-99ce-cc07172cfea4

📥 Commits

Reviewing files that changed from the base of the PR and between e6778b5 and e1d52dd.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • pubspec.yaml

📝 Walkthrough

Walkthrough

Adds explicit context.mounted guards after awaited asynchronous operations across multiple modules and adds a path: ^1.9.1 dependency in pubspec.yaml to prevent UI updates when widgets are unmounted.

Changes

Cohort / File(s) Summary
Detail Route Module
lib/app/modules/detailRoute/controllers/detail_route_controller.dart
Guards showDetailsPageTour with context.mounted before showing the coach mark; replaces arrow-style inline callback with explicit conditional block.
Home Module
lib/app/modules/home/views/add_task_bottom_sheet_new.dart, lib/app/modules/home/views/home_page_floating_action_button.dart
Adds context.mounted checks after key awaits (task save flows, SharedPreferences, and bottom sheet completion) to early-return if unmounted.
Manage Task Champion Creds
lib/app/modules/manage_task_champion_creds/views/manage_task_champion_creds_view.dart
Inserts context.mounted guard after async save before showing SnackBars.
Profile Module
lib/app/modules/profile/controllers/profile_controller.dart, lib/app/modules/profile/views/deleteprofiledialog.dart, lib/app/modules/profile/views/profile_view.dart
Adds mounted-context guards in profile tour callback, profile deletion (success/error), task export, and config copy flows to avoid UI actions on disposed contexts.
Reports Module
lib/app/modules/reports/controllers/reports_controller.dart
Adds context.mounted check inside async callback for SaveTourStatus.getReportsTourStatus() before evaluating value and showing tutorial coach mark.
Date Picker Utility
lib/app/utils/add_task_dialogue/date_picker_input.dart
Adds context.mounted checks after showDatePicker and showTimePicker awaits to avoid operating on unmounted widgets.
Dependencies
pubspec.yaml
Adds dependency entry: path: ^1.9.1.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 I tunneled through async night and day,

I checked if contexts still could stay,
Await returned — I peered around,
If mounted not, I left the ground,
A little hop, no crash today.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding context.mounted checks to address use_build_context_synchronously warnings across UI components.
Description check ✅ Passed The description is comprehensive and well-structured, explaining the problem, solution approach, affected files, and verification method. However, it does not include a 'Fixes #(issue_no)' section or a completed checklist as specified in the template.
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
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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

Tip

You can make CodeRabbit's review stricter and more nitpicky using the `assertive` profile, if that's what you prefer.

Change the reviews.profile setting to assertive to make CodeRabbit's nitpick more issues in your PRs.

Copy link

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/app/modules/profile/views/deleteprofiledialog.dart (1)

20-24: ⚠️ Potential issue | 🟠 Major

Use a stable parent context for post-pop Snackbar.

Line 70 pops the dialog, then Line 71 uses the same dialog context for ScaffoldMessenger without a mounted check. The build() method parameter shadows the field, so the code uses the dialog's own context after it's been dismissed. Use the parent context field instead for both the mounted check at line 62 and the SnackBar call at line 71, making the success path consistent with the error handling at lines 81–82.

Suggested fix
 class DeleteProfileDialog extends StatelessWidget {
   const DeleteProfileDialog({
     required this.profile,
-    required this.context,
+    required this.parentContext,
     required this.profiles,
     required this.profileName,
     super.key,
   });

   final String profile;
-  final BuildContext context;
+  final BuildContext parentContext;
   final RxMap<dynamic, dynamic> profiles;
   final String? profileName;
   `@override`
   Widget build(BuildContext context) {
@@
                     var splashController = Get.find<SplashController>();
                     await splashController.deleteProfile(profile);
-                    if (!context.mounted) return;
+                    if (!parentContext.mounted) return;
@@
                     Get.back();
-                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                    ScaffoldMessenger.of(parentContext).showSnackBar(SnackBar(
@@
                   } catch (e) {
-                    if (!context.mounted) return;
-                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                    if (!parentContext.mounted) return;
+                    ScaffoldMessenger.of(parentContext).showSnackBar(SnackBar(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/app/modules/profile/views/deleteprofiledialog.dart` around lines 20 - 24,
The dialog is using the build() parameter `context` (which shadows the field)
for the post-pop SnackBar and mounted check; change those uses to the stable
parent `context` field instead. Concretely, in the success path after calling
`Navigator.pop(context)` and before/when calling
`ScaffoldMessenger.of(...).showSnackBar(...)`, replace the build parameter
`context` with the widget field `context` (e.g., `this.context`) and use that
same field for the mounted check that currently references the build `context`,
so the mounted check and `ScaffoldMessenger.of(...)` both use the stable parent
`context` (leaving error-path usage that already uses the field unchanged).
🧹 Nitpick comments (1)
lib/app/modules/home/views/add_task_bottom_sheet_new.dart (1)

399-423: Pre-existing pattern: SnackBar shown after navigation.

The ScaffoldMessenger.of(context).showSnackBar() at lines 408-423 is called after Get.back() at line 399. While this typically works because ScaffoldMessenger resolves from the parent Scaffold (still mounted), showing feedback after closing the bottom sheet is an unusual pattern. The same applies to onSaveButtonClickedForReplica (lines 480-503).

Consider moving the SnackBar display before Get.back(), or using a callback/event-based approach to show feedback from the parent widget.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/app/modules/home/views/add_task_bottom_sheet_new.dart` around lines 399 -
423, The SnackBar is shown after navigating back via Get.back(), which can be
fragile; change both places (the Get.back() call and the matching
ScaffoldMessenger.of(context).showSnackBar(...) block) so the SnackBar is
displayed before calling Get.back(), or alternatively move the showSnackBar
logic out of this bottom-sheet view and trigger it via a callback/event to the
parent (see onSaveButtonClickedForReplica as another occurrence); ensure you
call ScaffoldMessenger.of(context).showSnackBar(...) while the bottom-sheet’s
Scaffold context is still valid or invoke a parent callback that the parent uses
to show the SnackBar and then call Get.back().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@lib/app/modules/profile/views/deleteprofiledialog.dart`:
- Around line 20-24: The dialog is using the build() parameter `context` (which
shadows the field) for the post-pop SnackBar and mounted check; change those
uses to the stable parent `context` field instead. Concretely, in the success
path after calling `Navigator.pop(context)` and before/when calling
`ScaffoldMessenger.of(...).showSnackBar(...)`, replace the build parameter
`context` with the widget field `context` (e.g., `this.context`) and use that
same field for the mounted check that currently references the build `context`,
so the mounted check and `ScaffoldMessenger.of(...)` both use the stable parent
`context` (leaving error-path usage that already uses the field unchanged).

---

Nitpick comments:
In `@lib/app/modules/home/views/add_task_bottom_sheet_new.dart`:
- Around line 399-423: The SnackBar is shown after navigating back via
Get.back(), which can be fragile; change both places (the Get.back() call and
the matching ScaffoldMessenger.of(context).showSnackBar(...) block) so the
SnackBar is displayed before calling Get.back(), or alternatively move the
showSnackBar logic out of this bottom-sheet view and trigger it via a
callback/event to the parent (see onSaveButtonClickedForReplica as another
occurrence); ensure you call ScaffoldMessenger.of(context).showSnackBar(...)
while the bottom-sheet’s Scaffold context is still valid or invoke a parent
callback that the parent uses to show the SnackBar and then call Get.back().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e2a0ed9-0778-49a1-98de-84160ef64e6b

📥 Commits

Reviewing files that changed from the base of the PR and between f058b4a and e6778b5.

📒 Files selected for processing (9)
  • lib/app/modules/detailRoute/controllers/detail_route_controller.dart
  • lib/app/modules/home/views/add_task_bottom_sheet_new.dart
  • lib/app/modules/home/views/home_page_floating_action_button.dart
  • lib/app/modules/manage_task_champion_creds/views/manage_task_champion_creds_view.dart
  • lib/app/modules/profile/controllers/profile_controller.dart
  • lib/app/modules/profile/views/deleteprofiledialog.dart
  • lib/app/modules/profile/views/profile_view.dart
  • lib/app/modules/reports/controllers/reports_controller.dart
  • lib/app/utils/add_task_dialogue/date_picker_input.dart

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.

1 participant