Add local mood reminders + stress/doctor prompts + improve admin UX + maya for admin#12
Add local mood reminders + stress/doctor prompts + improve admin UX + maya for admin#12Ashish-Kumar-Dash wants to merge 9 commits intoOpenLake:mainfrom
Conversation
WalkthroughThis PR adds a centralized friendly error formatter, a NotificationService with timezone-aware daily reminders and tap routing, an admin-facing Maya chat with patient context, chat-service admin-mode prompts, mood-based notification scheduling, a patient search UI, and related navigation/plugin wiring. Changes
Sequence DiagramssequenceDiagram
participant Admin as Admin User
participant Page as AdminMayaChatPage
participant Supabase as Supabase DB
participant ChatSvc as ChatService
participant LLM as Maya/LLM
Admin->>Page: Open admin chat page
Page->>Supabase: Load patient profiles
Supabase-->>Page: Return patient list
Page->>Page: Render dropdown
Admin->>Page: Select patient
Page->>ChatSvc: Create ChatService(isAdminMode=true, targetPatientId)
ChatSvc->>Supabase: Fetch patient profile
Supabase-->>ChatSvc: Profile data
ChatSvc->>ChatSvc: Build admin prompt + PATIENT SUMMARY
ChatSvc-->>Page: Ready with context message
Admin->>Page: Send message
Page->>Page: Append user message, disable input
Page->>ChatSvc: sendMessage(userMessage)
ChatSvc->>LLM: Request with admin prompt + context
LLM-->>ChatSvc: Assistant response
ChatSvc-->>Page: Assistant message
Page->>Page: Append assistant message, re-enable input
sequenceDiagram
participant MoodPage as Mood Tracker
participant DB as Supabase
participant NotifSvc as NotificationService
participant Device as OS Scheduler
MoodPage->>DB: Fetch recent mood entries
DB-->>MoodPage: Mood records
MoodPage->>MoodPage: Compute avg mood and low-mood streak
alt Mood logged today
MoodPage->>NotifSvc: cancel(id:1) daily mood reminder
NotifSvc-->>Device: Cancel scheduled notification
else No mood today
MoodPage->>NotifSvc: scheduleDailyReminder(id:1,...)
NotifSvc-->>Device: Schedule daily notification (timezone-aware)
end
alt Low mood detected (avg ≤ 2.5 or streak ≥ 3)
MoodPage->>NotifSvc: scheduleDailyReminder(id:2,...), scheduleDailyReminder(id:3,...)
else Mood improving
MoodPage->>NotifSvc: cancel(id:2), cancel(id:3)
end
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment Tip You can disable sequence diagrams in the walkthrough.Disable the |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
flutter_app/lib/pages/mood_tracker.dart (1)
1354-1581:⚠️ Potential issue | 🔴 CriticalRemove the orphaned
buildblock at file scope.The file has already closed all widget classes before this section starts, so this trailing
@override Widget build(...)block is top-level Dart and the file will not compile.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@flutter_app/lib/pages/mood_tracker.dart` around lines 1354 - 1581, There is an orphaned top-level "@override Widget build(BuildContext context)" block that should be deleted; remove the entire trailing build(...) {...} block shown in the diff (from the "@override Widget build(BuildContext context)" line through its matching closing brace) so the file no longer contains a standalone build function, and then verify any surrounding widget classes (e.g., the Mood tracker page class) still have their correct build methods and balanced braces.
🧹 Nitpick comments (4)
flutter_app/lib/pages/article_page.dart (1)
60-60: Provide page-specific fallback copy for inline error panels.Since these errors are displayed persistently in-page, pass a fallback so users don’t see technical/non-actionable text when parsing fails.
Suggested update
- _popularError = friendlyErrorMessage(e); + _popularError = friendlyErrorMessage( + e, + fallback: 'Unable to load popular articles right now. Please try again.', + ); ... - _categoryError = friendlyErrorMessage(e); + _categoryError = friendlyErrorMessage( + e, + fallback: 'Unable to load this topic right now. Please try again.', + );Also applies to: 93-93
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@flutter_app/lib/pages/article_page.dart` at line 60, Replace the raw technical fallback by passing a page-specific fallback string into friendlyErrorMessage wherever _popularError is set (e.g., the assignments in ArticlePage that currently read "_popularError = friendlyErrorMessage(e);"); update both occurrences (around the current lines where _popularError is assigned) to call friendlyErrorMessage(e, "Unable to load popular articles right now. Please try again later.") or similarly worded page-specific copy so the inline error panel shows user-friendly, actionable text.flutter_app/lib/pages/admin_maya_chat_page.dart (2)
304-343: Hardcoded white background breaks dark mode.The input container uses
color: Colors.white(line 309), which will clash with dark theme text colors and create poor contrast. Consider using a theme-aware color.♻️ Suggested fix
child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), - color: Colors.white, + color: Theme.of(context).cardColor, child: Row(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@flutter_app/lib/pages/admin_maya_chat_page.dart` around lines 304 - 343, The Container around the input bar uses a hardcoded color (color: Colors.white) which breaks dark mode; replace that hardcoded value with a theme-aware color (e.g., use Theme.of(context).colorScheme.surface or Theme.of(context).cardColor/scaffoldBackgroundColor) so the background adapts to light/dark themes, and ensure the inner TextField fillColor (fillColor: Colors.grey.shade200) is also switched to a theme-aware alternative (e.g., Theme.of(context).colorScheme.background or a shade derived from Theme) to maintain proper contrast; update the Container and TextField properties in the build where SafeArea/Container/TextField are defined.
62-67: Consider adding user feedback when patient list fails to load.The error is silently swallowed, leaving the dropdown empty without explanation. A snackbar or inline message would help doctors understand why no patients appear.
♻️ Suggested addition
} catch (e) { setState(() { _patients = []; _isFetchingPatients = false; }); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Unable to load patients: ${friendlyErrorMessage(e)}'), + ), + ); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@flutter_app/lib/pages/admin_maya_chat_page.dart` around lines 62 - 67, The catch block that clears _patients and sets _isFetchingPatients to false silently swallows errors; update the error handling in the fetch method (the catch in the function that calls setState with _patients/_isFetchingPatients) to both surface user feedback and preserve existing state updates: set a new state field like _patientFetchError (or reuse an existing error state) inside setState, and show a user-facing message via ScaffoldMessenger.of(context).showSnackBar(...) or display an inline widget bound to _patientFetchError so doctors see why the patient list failed to load; keep the existing _patients = [] and _isFetchingPatients = false updates.flutter_app/lib/main.dart (1)
21-25: Consider loading Supabase config from environment variables.The
.envfile is already loaded (line 18), but the Supabase URL andanonKeyare hardcoded. While the anon key is designed to be public, loading both values fromdotenv.envwould improve consistency and make environment switching easier (dev/staging/prod).♻️ Suggested refactor
// Initialize Supabase await Supabase.initialize( - url: 'https://prvbbbnsizxxfxreokov.supabase.co', - anonKey: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBydmJiYm5zaXp4eGZ4cmVva292Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzA4OTMwODgsImV4cCI6MjA4NjQ2OTA4OH0.GOqP5k0AnGzqse5loXuPz9BkfGCUdJgApUL9QBVw0es', + url: dotenv.env['SUPABASE_URL'] ?? '', + anonKey: dotenv.env['SUPABASE_ANON_KEY'] ?? '', );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@flutter_app/lib/main.dart` around lines 21 - 25, Replace the hardcoded Supabase URL and anon key in the Supabase.initialize call with values read from the loaded environment (dotenv.env); update the call in main.dart where Supabase.initialize(...) is invoked so it uses dotenv.env['SUPABASE_URL'] and dotenv.env['SUPABASE_ANON_KEY'] (or sensible env var names) instead of the literal strings, and add a fallback or assert that these env values are present before calling Supabase.initialize to avoid null values at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@flutter_app/lib/core/error_messages.dart`:
- Around line 7-21: Make the matching case-insensitive and sanitize common
exception prefixes before returning to users: normalize the captured message
(e.g., store a trimmed copy and a lowercase version) and use the lowercase form
for contains checks instead of the current case-sensitive checks on message;
then strip common leading prefixes such as "Exception:", "Error:",
"FormatException:", "SocketException:" (and any alphabetic word followed by a
colon) from the user-facing message variable so raw exception labels aren’t
shown; ensure the function still returns fallback when matches occur and
otherwise returns the sanitized message.
In `@flutter_app/lib/main.dart`:
- Around line 27-28: The app currently only registers
NotificationService.instance.onDidReceiveNotificationResponse (via
NotificationService.initialize), so taps on notifications when the app is
terminated won't navigate on cold-start; to fix, add cold-launch handling either
by (A) implementing and registering a top-level `@pragma`('vm:entry-point')
callback and pass it to NotificationService (register it with
onDidReceiveBackgroundNotificationResponse) or (B) after startup call
NotificationService.instance.getNotificationAppLaunchDetails() (or
flutter_local_notifications' getNotificationAppLaunchDetails()) in main.dart
right after initialize() and requestPermissions() and handle any returned
payload to perform the navigation/deep-link; update NotificationService to
expose a method to process that payload and reuse the same navigation logic as
onDidReceiveNotificationResponse.
In `@flutter_app/lib/pages/mood_tracker.dart`:
- Around line 183-188: The current heuristic computes avgMood over all _entries
and _lowMoodStreak() counts only streaks that reach today, which lets old data
mask recent downturns and drops streaks when today is missing; change both
avgMood and the streak logic to operate on a bounded recent window (e.g., last N
days) and make the streak anchored to the most recent logged entry rather than
today. Specifically, replace the global avgMood calculation (the fold over
_entries) with an average over a slice/filter of _entries limited to the recent
window, and update _lowMoodStreak() to compute consecutive low days ending at
the latest entry date (not necessarily today); then recompute
shouldSuggestStress from that windowed avg and the anchored streak. Ensure
references to _entries, avgMood, _lowMoodStreak(), and shouldSuggestStress are
updated consistently (also apply the same windowed approach to the other related
block around the 214-233 region).
- Around line 156-164: The code currently cancels the recurring reminder
(NotificationService.cancel(1)) before re-scheduling in
_load()/_updateReminders(), which removes the user's daily 8PM reminder if they
don't reopen the screen; instead, stop cancelling the reminder
unconditionally—change _updateReminders() to check whether the repeating
reminder is already scheduled (use a NotificationService method like isScheduled
or listScheduledNotifications) and only call
NotificationService.scheduleDailyReminder(...) when it is not present, and
remove the unconditional NotificationService.cancel(1) call; reference _load(),
_updateReminders(), NotificationService.scheduleDailyReminder and
NotificationService.cancel to locate and update the logic.
- Around line 171-177: Replace the privacy-revealing notification text used in
calls to NotificationService.instance.scheduleDailyReminder (e.g., the title
'Quick mood check-in' and body "You haven’t tracked your mood today. Tap to log
it!") with neutral, non-health-specific copy (for example a generic reminder
title like "Reminder" and a body like "Tap to open the app.") in both
occurrences (the call around scheduleDailyReminder at the 171–177 block and the
later 191–207 block); keep the payload/deep link as-is so the app can reveal the
specific mood-tracking action after unlock.
In `@flutter_app/lib/services/chat_service.dart`:
- Around line 130-166: The patient context currently sent to OpenRouter lacks an
explicit non-HIPAA disclosure; update the consent/privacy text that is
concatenated into the prompt (where _adminPrompt + _basePrompt + patientContext
are assembled in chat_service.dart) to add an explicit, prominent warning
stating that OpenRouter is not HIPAA-compliant, does not sign BAAs, that
consenting users accept this compliance gap and that sharing real patient data
will transmit protected health information to a non-HIPAA-covered entity; also
add a recommendation to avoid sending regulated patient data in production and
to consider HIPAA/BAA-capable providers as alternatives. Ensure this disclosure
appears before any patient details are appended to patientContext and is
triggered/visible whenever sensitive fields (medical_conditions,
current_medications, mental_health_concerns, therapy_history) are non-empty so
users see the warning prior to consenting.
In `@flutter_app/lib/services/notification_service.dart`:
- Around line 51-60: The requestPermissions() method only handles iOS—add
Android 13+ runtime permission handling: inside requestPermissions(), detect
Android (Platform.isAndroid) and check SDK_INT >= 33, then request the
POST_NOTIFICATIONS permission (e.g., via the permission_handler package using
Permission.notification or Permission.postNotifications) and handle the
granted/denied result before proceeding; also ensure you declare
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> in
AndroidManifest.xml for apps targeting API 33+. Reference the
requestPermissions() method and the _plugin field when locating where to add the
Android branch.
- Around line 102-123: The call to _plugin.zonedSchedule is missing the required
androidScheduleMode parameter for flutter_local_notifications v13+; replace the
androidAllowWhileIdle: true argument in the zonedSchedule invocation with
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle so the call
signature matches the newer API and preserves the "allow while idle" behavior
(update the zonedSchedule call in notification_service.dart where
_plugin.zonedSchedule is invoked).
---
Outside diff comments:
In `@flutter_app/lib/pages/mood_tracker.dart`:
- Around line 1354-1581: There is an orphaned top-level "@override Widget
build(BuildContext context)" block that should be deleted; remove the entire
trailing build(...) {...} block shown in the diff (from the "@override Widget
build(BuildContext context)" line through its matching closing brace) so the
file no longer contains a standalone build function, and then verify any
surrounding widget classes (e.g., the Mood tracker page class) still have their
correct build methods and balanced braces.
---
Nitpick comments:
In `@flutter_app/lib/main.dart`:
- Around line 21-25: Replace the hardcoded Supabase URL and anon key in the
Supabase.initialize call with values read from the loaded environment
(dotenv.env); update the call in main.dart where Supabase.initialize(...) is
invoked so it uses dotenv.env['SUPABASE_URL'] and
dotenv.env['SUPABASE_ANON_KEY'] (or sensible env var names) instead of the
literal strings, and add a fallback or assert that these env values are present
before calling Supabase.initialize to avoid null values at runtime.
In `@flutter_app/lib/pages/admin_maya_chat_page.dart`:
- Around line 304-343: The Container around the input bar uses a hardcoded color
(color: Colors.white) which breaks dark mode; replace that hardcoded value with
a theme-aware color (e.g., use Theme.of(context).colorScheme.surface or
Theme.of(context).cardColor/scaffoldBackgroundColor) so the background adapts to
light/dark themes, and ensure the inner TextField fillColor (fillColor:
Colors.grey.shade200) is also switched to a theme-aware alternative (e.g.,
Theme.of(context).colorScheme.background or a shade derived from Theme) to
maintain proper contrast; update the Container and TextField properties in the
build where SafeArea/Container/TextField are defined.
- Around line 62-67: The catch block that clears _patients and sets
_isFetchingPatients to false silently swallows errors; update the error handling
in the fetch method (the catch in the function that calls setState with
_patients/_isFetchingPatients) to both surface user feedback and preserve
existing state updates: set a new state field like _patientFetchError (or reuse
an existing error state) inside setState, and show a user-facing message via
ScaffoldMessenger.of(context).showSnackBar(...) or display an inline widget
bound to _patientFetchError so doctors see why the patient list failed to load;
keep the existing _patients = [] and _isFetchingPatients = false updates.
In `@flutter_app/lib/pages/article_page.dart`:
- Line 60: Replace the raw technical fallback by passing a page-specific
fallback string into friendlyErrorMessage wherever _popularError is set (e.g.,
the assignments in ArticlePage that currently read "_popularError =
friendlyErrorMessage(e);"); update both occurrences (around the current lines
where _popularError is assigned) to call friendlyErrorMessage(e, "Unable to load
popular articles right now. Please try again later.") or similarly worded
page-specific copy so the inline error panel shows user-friendly, actionable
text.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fb126115-e0eb-467a-8c28-24f87bf201c4
⛔ Files ignored due to path filters (1)
flutter_app/pubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (21)
flutter_app/lib/core/error_messages.dartflutter_app/lib/main.dartflutter_app/lib/pages/admin_dashboard.dartflutter_app/lib/pages/admin_maya_chat_page.dartflutter_app/lib/pages/ai_chat.dartflutter_app/lib/pages/article_page.dartflutter_app/lib/pages/doctor_chat_page.dartflutter_app/lib/pages/maya_consent_page.dartflutter_app/lib/pages/meeting_requests_page.dartflutter_app/lib/pages/mood_tracker.dartflutter_app/lib/pages/onboarding_page.dartflutter_app/lib/pages/online_meet_page.dartflutter_app/lib/pages/past_interactions_page.dartflutter_app/lib/pages/past_meetings_page.dartflutter_app/lib/pages/patient_list_page.dartflutter_app/lib/pages/profile_page.dartflutter_app/lib/pages/scheduled_meetings_page.dartflutter_app/lib/services/chat_service.dartflutter_app/lib/services/notification_service.dartflutter_app/macos/Flutter/GeneratedPluginRegistrant.swiftflutter_app/pubspec.yaml
| final message = error.toString(); | ||
|
|
||
| // Network / connectivity issues we want to hide from users | ||
| if (message.contains('Connection reset') || | ||
| message.contains('Failed host lookup') || | ||
| message.contains('Network is unreachable') || | ||
| message.contains('Connection refused') || | ||
| message.contains('Connection timed out') || | ||
| message.contains('SocketException') || | ||
| message.contains('HttpException')) { | ||
| return fallback ?? | ||
| 'Oops—looks like we hit a connection hiccup. Please try again in a moment — don\'t fret, it\'s on us.'; | ||
| } | ||
|
|
||
| return message; |
There was a problem hiding this comment.
Harden matching and sanitize common exception prefixes.
Line 10–16 currently depends on case-sensitive matching, and Line 21 can still surface raw strings like Exception: ... to users. That weakens the “friendly” behavior in real-world failures.
Proposed patch
String friendlyErrorMessage(Object error, {String? fallback}) {
- final message = error.toString();
+ final message = error.toString().trim();
+ final normalized = message.toLowerCase();
// Network / connectivity issues we want to hide from users
- if (message.contains('Connection reset') ||
- message.contains('Failed host lookup') ||
- message.contains('Network is unreachable') ||
- message.contains('Connection refused') ||
- message.contains('Connection timed out') ||
- message.contains('SocketException') ||
- message.contains('HttpException')) {
+ if (normalized.contains('connection reset') ||
+ normalized.contains('failed host lookup') ||
+ normalized.contains('network is unreachable') ||
+ normalized.contains('connection refused') ||
+ normalized.contains('connection timed out') ||
+ normalized.contains('socketexception') ||
+ normalized.contains('httpexception')) {
return fallback ??
'Oops—looks like we hit a connection hiccup. Please try again in a moment — don\'t fret, it\'s on us.';
}
+ if (message.startsWith('Exception: ')) {
+ return message.replaceFirst('Exception: ', '');
+ }
+
return message;
}📝 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.
| final message = error.toString(); | |
| // Network / connectivity issues we want to hide from users | |
| if (message.contains('Connection reset') || | |
| message.contains('Failed host lookup') || | |
| message.contains('Network is unreachable') || | |
| message.contains('Connection refused') || | |
| message.contains('Connection timed out') || | |
| message.contains('SocketException') || | |
| message.contains('HttpException')) { | |
| return fallback ?? | |
| 'Oops—looks like we hit a connection hiccup. Please try again in a moment — don\'t fret, it\'s on us.'; | |
| } | |
| return message; | |
| final message = error.toString().trim(); | |
| final normalized = message.toLowerCase(); | |
| // Network / connectivity issues we want to hide from users | |
| if (normalized.contains('connection reset') || | |
| normalized.contains('failed host lookup') || | |
| normalized.contains('network is unreachable') || | |
| normalized.contains('connection refused') || | |
| normalized.contains('connection timed out') || | |
| normalized.contains('socketexception') || | |
| normalized.contains('httpexception')) { | |
| return fallback ?? | |
| 'Oops—looks like we hit a connection hiccup. Please try again in a moment — don\'t fret, it\'s on us.'; | |
| } | |
| if (message.startsWith('Exception: ')) { | |
| return message.replaceFirst('Exception: ', ''); | |
| } | |
| return message; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/core/error_messages.dart` around lines 7 - 21, Make the
matching case-insensitive and sanitize common exception prefixes before
returning to users: normalize the captured message (e.g., store a trimmed copy
and a lowercase version) and use the lowercase form for contains checks instead
of the current case-sensitive checks on message; then strip common leading
prefixes such as "Exception:", "Error:", "FormatException:", "SocketException:"
(and any alphabetic word followed by a colon) from the user-facing message
variable so raw exception labels aren’t shown; ensure the function still returns
fallback when matches occur and otherwise returns the sanitized message.
| await NotificationService.instance.initialize(navigatorKey: navigatorKey); | ||
| await NotificationService.instance.requestPermissions(); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
flutter_local_notifications onDidReceiveBackgroundNotificationResponse setup
💡 Result:
To set up onDidReceiveBackgroundNotificationResponse for flutter_local_notifications, follow these steps for handling notification taps/responses when the app is in the background or terminated, particularly for notification actions. ## Dart Code Setup 1. Create a top-level or static function marked with @pragma('vm:entry-point'): @pragma('vm:entry-point') void notificationTapBackground(NotificationResponse notificationResponse) { debugPrint('Background notification tapped: ${notificationResponse.payload}'); // Handle the response here (e.g., process payload, schedule tasks) // Note: Cannot access Flutter UI directly; use isolates or shared storage } 2. Initialize the plugin, passing the function directly (not wrapped in a lambda): final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin; await flutterLocalNotificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse response) { // Foreground/background (app running) handling }, onDidReceiveBackgroundNotificationResponse: notificationTapBackground, // Direct reference ); ## Platform-Specific Setup ### Android - No additional configuration needed. - Ensure proper AndroidManifest.xml for scheduled notifications if applicable. - Add receiver for actions if using notification actions: ### iOS (Critical for background handling) Required for background isolate support when app is terminated: 1. In ios/Runner/AppDelegate.swift (or .m): import Flutter import flutter_local_notifications // Add this import // Add this function (Objective-C example for .m equivalent in Swift): FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in GeneratedPluginRegistrant.register(with: registry) } 2. In application(_:didFinishLaunchingWithOptions:): [GeneratedPluginRegistrant registerWithRegistry:self]; FlutterLocalNotificationsPlugin.setPluginRegistrantCallback(registerPlugins); 3. Update Info.plist with background modes if needed: UIBackgroundModes fetch remote-notification ## Key Notes - This callback is primarily for notification actions (e.g., reply, mark as read) when app is terminated/background isolate. - For simple notification taps when terminated: Use getNotificationAppLaunchDetails after app starts. - Callback runs in background isolate: no UI access, keep operations lightweight. - Test on physical devices; simulators may not trigger background callbacks properly. - Latest package version (21.0.0 as of 2026) recommended. Common errors: - Wrapping function in lambda: use direct reference. - Missing iOS AppDelegate setup: background won't work. - Not top-level/static: compile error.
Citations:
- 1: https://stackoverflow.com/questions/76804712/how-can-i-set-the-ondidreceivebackgroundnotificationresponse-property-of-flutter
- 2: https://pub.dev/documentation/flutter_local_notifications/latest
- 3: https://pub.dev/packages/flutter_local_notifications
- 4: https://stackoverflow.com/questions/77895149/flutter-how-to-handle-notifications-actions-with-app-in-background
- 5: onDidReceiveBackgroundNotificationResponse callback is not getting navigate or not triggered function when tapped on notification MaikuB/flutter_local_notifications#2011
- 6: https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/README.md
- 7: onDidReceiveBackgroundNotificationResponse callback doesn't in background MaikuB/flutter_local_notifications#2315
🏁 Script executed:
fd -e dart -e swift -e java | grep -i notification | head -20Repository: OpenLake/GetWelPlus
Length of output: 113
🏁 Script executed:
find . -type f -name "*.dart" | xargs rg "class NotificationService" -lRepository: OpenLake/GetWelPlus
Length of output: 115
🏁 Script executed:
rg "onDidReceiveBackgroundNotificationResponse" --type dartRepository: OpenLake/GetWelPlus
Length of output: 45
🏁 Script executed:
cat -n ./flutter_app/lib/services/notification_service.dartRepository: OpenLake/GetWelPlus
Length of output: 4985
🏁 Script executed:
rg "getNotificationAppLaunchDetails" ./flutter_app --type dartRepository: OpenLake/GetWelPlus
Length of output: 45
Notification taps won't navigate when the app is terminated.
The NotificationService only registers onDidReceiveNotificationResponse, which handles taps when the app is running. For taps when the app is fully terminated, flutter_local_notifications requires either:
- A separate
@pragma('vm:entry-point')top-level callback passed toonDidReceiveBackgroundNotificationResponse, or - Checking
getNotificationAppLaunchDetails()after app startup for cold-launch navigation
If cold-start deep-linking from notifications is a requirement, consider adding one of these handlers. Otherwise, this is acceptable for MVP.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/main.dart` around lines 27 - 28, The app currently only
registers NotificationService.instance.onDidReceiveNotificationResponse (via
NotificationService.initialize), so taps on notifications when the app is
terminated won't navigate on cold-start; to fix, add cold-launch handling either
by (A) implementing and registering a top-level `@pragma`('vm:entry-point')
callback and pass it to NotificationService (register it with
onDidReceiveBackgroundNotificationResponse) or (B) after startup call
NotificationService.instance.getNotificationAppLaunchDetails() (or
flutter_local_notifications' getNotificationAppLaunchDetails()) in main.dart
right after initialize() and requestPermissions() and handle any returned
payload to perform the navigation/deep-link; update NotificationService to
expose a method to process that payload and reuse the same navigation logic as
onDidReceiveNotificationResponse.
| Future<void> _load() async { | ||
| final entries = await _loadEntries(); | ||
| if (mounted) setState(() { _entries = entries; _loading = false; }); | ||
| if (mounted) { | ||
| setState(() { | ||
| _entries = entries; | ||
| _loading = false; | ||
| }); | ||
| await _updateReminders(); | ||
| } |
There was a problem hiding this comment.
Don’t cancel the only recurring mood reminder from this page.
NotificationService.scheduleDailyReminder(...) is a repeating schedule. Once cancel(1) runs, tomorrow’s 8 PM reminder is gone too, and this code only recreates it when _load() runs again. A user who logs mood today and never reopens this screen tomorrow will stop getting reminders entirely.
Also applies to: 167-181
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/pages/mood_tracker.dart` around lines 156 - 164, The code
currently cancels the recurring reminder (NotificationService.cancel(1)) before
re-scheduling in _load()/_updateReminders(), which removes the user's daily 8PM
reminder if they don't reopen the screen; instead, stop cancelling the reminder
unconditionally—change _updateReminders() to check whether the repeating
reminder is already scheduled (use a NotificationService method like isScheduled
or listScheduledNotifications) and only call
NotificationService.scheduleDailyReminder(...) when it is not present, and
remove the unconditional NotificationService.cancel(1) call; reference _load(),
_updateReminders(), NotificationService.scheduleDailyReminder and
NotificationService.cancel to locate and update the logic.
| await NotificationService.instance.scheduleDailyReminder( | ||
| id: 1, | ||
| title: 'Quick mood check-in', | ||
| body: "You haven’t tracked your mood today. Tap to log it!", | ||
| payload: 'mood', | ||
| hour: 20, | ||
| minute: 0, |
There was a problem hiding this comment.
Use neutral notification copy for health-related reminders.
These titles and bodies disclose mental-health context on the lock screen. In a wellness app, prompts like “Feeling stressed?” and “Book a quick session with a doctor...” are privacy-sensitive. Prefer generic wording and let the deep link reveal the specific action after unlock.
🔒 Suggested wording
- title: 'Quick mood check-in',
- body: "You haven’t tracked your mood today. Tap to log it!",
+ title: 'GetWel+ reminder',
+ body: 'Open the app for today’s check-in.',
- title: 'Feeling stressed?',
- body: 'Take a quick stress check and see how you’re doing.',
+ title: 'GetWel+ reminder',
+ body: 'Open the app for your next check-in.',
- title: 'Need a check-in?',
- body: 'Book a quick session with a doctor if you’re feeling overwhelmed.',
+ title: 'GetWel+ reminder',
+ body: 'Open the app to review support options.',Also applies to: 191-207
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/pages/mood_tracker.dart` around lines 171 - 177, Replace the
privacy-revealing notification text used in calls to
NotificationService.instance.scheduleDailyReminder (e.g., the title 'Quick mood
check-in' and body "You haven’t tracked your mood today. Tap to log it!") with
neutral, non-health-specific copy (for example a generic reminder title like
"Reminder" and a body like "Tap to open the app.") in both occurrences (the call
around scheduleDailyReminder at the 171–177 block and the later 191–207 block);
keep the payload/deep link as-is so the app can reveal the specific
mood-tracking action after unlock.
| // If mood has been low recently, suggest a stress check. | ||
| final longLowMood = _lowMoodStreak(); | ||
| final avgMood = _entries.isEmpty | ||
| ? 5.0 | ||
| : _entries.fold(0, (s, e) => s + e.moodLevel) / _entries.length; | ||
| final shouldSuggestStress = avgMood <= 2.5 || longLowMood >= 3; |
There was a problem hiding this comment.
The low-mood heuristic doesn’t actually track recent trends.
avgMood is computed over the entire history, while _lowMoodStreak() only counts streaks that reach today. Older positive months can suppress alerts during a current downturn, and a user with several low days ending yesterday drops to 0 as soon as today is missing. Use a bounded recent window and anchor the streak to the latest logged day instead.
Also applies to: 214-233
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/pages/mood_tracker.dart` around lines 183 - 188, The current
heuristic computes avgMood over all _entries and _lowMoodStreak() counts only
streaks that reach today, which lets old data mask recent downturns and drops
streaks when today is missing; change both avgMood and the streak logic to
operate on a bounded recent window (e.g., last N days) and make the streak
anchored to the most recent logged entry rather than today. Specifically,
replace the global avgMood calculation (the fold over _entries) with an average
over a slice/filter of _entries limited to the recent window, and update
_lowMoodStreak() to compute consecutive low days ending at the latest entry date
(not necessarily today); then recompute shouldSuggestStress from that windowed
avg and the anchored streak. Ensure references to _entries, avgMood,
_lowMoodStreak(), and shouldSuggestStress are updated consistently (also apply
the same windowed approach to the other related block around the 214-233
region).
| // build concise patient summary | ||
| final name = _patientProfile!['full_name'] ?? ''; | ||
| final age = _patientProfile!['age'] ?? ''; | ||
| final conditions = _patientProfile!['medical_conditions'] ?? ''; | ||
| final medications = _patientProfile!['current_medications'] ?? ''; | ||
| final concerns = _patientProfile!['mental_health_concerns'] ?? ''; | ||
| final therapyHistory = _patientProfile!['therapy_history'] ?? ''; | ||
|
|
||
| final contextParts = <String>[]; | ||
| if (name.toString().isNotEmpty) { | ||
| contextParts.add('Name: $name'); | ||
| } | ||
| if (age.toString().isNotEmpty && age != 0) { | ||
| contextParts.add('Age: $age'); | ||
| } | ||
| if (conditions.toString().isNotEmpty) { | ||
| contextParts.add('Medical conditions: $conditions'); | ||
| } | ||
| if (medications.toString().isNotEmpty) { | ||
| contextParts.add('Current medications: $medications'); | ||
| } | ||
| if (concerns.toString().isNotEmpty) { | ||
| contextParts.add('Main concerns: $concerns'); | ||
| } | ||
| if (therapyHistory.toString().isNotEmpty) { | ||
| contextParts.add('Therapy history: $therapyHistory'); | ||
| } | ||
|
|
||
| final patientContext = ''' | ||
|
|
||
| PATIENT SUMMARY (use for context when responding): | ||
| ${contextParts.join('. ')}. | ||
|
|
||
| When responding, remain respectful of privacy, keep tone supportive, and avoid making definitive medical diagnoses. | ||
| '''; | ||
|
|
||
| return _adminPrompt + _basePrompt + patientContext; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
OpenRouter HIPAA BAA compliance healthcare data
💡 Result:
OpenRouter does not offer HIPAA compliance or Business Associate Agreements (BAAs). It provides privacy features like Zero Data Retention (ZDR) routing to providers that do not log or train on prompts, opt-in prompt logging, metadata-only storage by default, and enterprise options such as EU in-region routing and SOC-2 compliance mentions, but no explicit HIPAA or BAA support in official docs. OpenRouter routes requests to third-party AI providers, each with their own policies (e.g., some like OpenAI offer BAAs separately). For healthcare data (PHI), users must ensure selected providers comply with HIPAA requirements, as OpenRouter itself does not act as a HIPAA business associate. Contact OpenRouter enterprise sales for custom needs, but no public evidence of BAA availability as of 2026-03-19.
Citations:
- 1: https://openrouter.ai/docs/features/privacy-and-logging
- 2: https://openrouter.ai/privacy
- 3: https://openrouter.ai/docs/guides/privacy/data-collection
- 4: https://openrouter.ai/docs/guides/features/zdr
- 5: https://openrouter.ai/docs/enterprise-quickstart
- 6: http://openrouter.ai/enterprise
- 7: https://www.openrouter.ai
🏁 Script executed:
# Search for context around the _buildSystemPrompt method and patient data handling
cd flutter_app && find . -name "*.dart" -type f | head -20Repository: OpenLake/GetWelPlus
Length of output: 673
🏁 Script executed:
# Look for consent mechanisms, demo flags, or synthetic data indicators
rg -i "demo|synthetic|test.*data|consent|hipaa|privacy" --type dart flutter_app/libRepository: OpenLake/GetWelPlus
Length of output: 3593
🏁 Script executed:
# Check the chat_service.dart file more broadly to understand data flow
wc -l flutter_app/lib/services/chat_service.dartRepository: OpenLake/GetWelPlus
Length of output: 109
🏁 Script executed:
# Check the consent page content and privacy disclosures
cat -n flutter_app/lib/pages/maya_consent_page.dartRepository: OpenLake/GetWelPlus
Length of output: 8930
🏁 Script executed:
# Check how AI chat page uses the consent flag
cat -n flutter_app/lib/pages/ai_chat.dart | head -100Repository: OpenLake/GetWelPlus
Length of output: 3486
🏁 Script executed:
# Verify if chatService respects the consent flag
rg "sharesMedicalData|maya_data_consent" flutter_app/lib/services/chat_service.dart -A 3 -B 3Repository: OpenLake/GetWelPlus
Length of output: 45
🏁 Script executed:
# Check how ChatService constructor uses the usePersonalData parameter
cat -n flutter_app/lib/services/chat_service.dart | head -50Repository: OpenLake/GetWelPlus
Length of output: 2294
🏁 Script executed:
# Search for usePersonalData usage in chat_service.dart
rg "usePersonalData" flutter_app/lib/services/chat_service.dart -B 2 -A 5Repository: OpenLake/GetWelPlus
Length of output: 1215
Strengthen compliance disclosures before production use with real patient data.
OpenRouter does not offer HIPAA Business Associate Agreements or BAA compliance. If users consent to share medical data (medical conditions, medications, mental health concerns), that data goes to OpenRouter and downstream providers without HIPAA protections. While the app properly implements an opt-in consent model and users can decline data sharing entirely, the privacy notice should explicitly state:
- OpenRouter is not HIPAA-compliant and does not sign BAAs
- Consenting users accept this compliance gap
- For real patient data, users should understand they are sharing health information with a non-HIPAA-covered entity
- Consider alternative AI providers with HIPAA/BAA support before handling regulated patient data in production
The current consent mechanism mitigates risk for users who decline, but does not resolve the underlying compliance constraint for users who consent.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/services/chat_service.dart` around lines 130 - 166, The
patient context currently sent to OpenRouter lacks an explicit non-HIPAA
disclosure; update the consent/privacy text that is concatenated into the prompt
(where _adminPrompt + _basePrompt + patientContext are assembled in
chat_service.dart) to add an explicit, prominent warning stating that OpenRouter
is not HIPAA-compliant, does not sign BAAs, that consenting users accept this
compliance gap and that sharing real patient data will transmit protected health
information to a non-HIPAA-covered entity; also add a recommendation to avoid
sending regulated patient data in production and to consider HIPAA/BAA-capable
providers as alternatives. Ensure this disclosure appears before any patient
details are appended to patientContext and is triggered/visible whenever
sensitive fields (medical_conditions, current_medications,
mental_health_concerns, therapy_history) are non-empty so users see the warning
prior to consenting.
| /// Requests platform permissions (mainly iOS/macOS) for notifications. | ||
| Future<void> requestPermissions() async { | ||
| final iosPlugin = | ||
| _plugin.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>(); | ||
| await iosPlugin?.requestPermissions( | ||
| alert: true, | ||
| badge: true, | ||
| sound: true, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
flutter_local_notifications Android 13 POST_NOTIFICATIONS permission
💡 Result:
For flutter_local_notifications on Android 13+, apps targeting API 33+ must declare the POST_NOTIFICATIONS permission in AndroidManifest.xml and request it at runtime using the plugin's API. Key steps: 1. Update android/app/build.gradle: compileSdkVersion 33 (or higher) targetSdkVersion 33 (or higher) 2. Add to android/app/src/main/AndroidManifest.xml (inside ): The plugin (v16+) already includes this minimally, but explicitly adding ensures compatibility. For scheduled notifications, add more like SCHEDULE_EXACT_ALARM. 3. Request permission in Dart code: final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin; final AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation; await androidImplementation?.requestNotificationsPermission; Without this, notifications won't show on Android 13+ devices as they're blocked by default. The plugin supports this since v17.0.1. Check permission status before showing notifications. Example app on pub.dev shows full AndroidManifest.xml setup. Use permission_handler for unified checks: await Permission.notification.request; Sources confirm this from official pub.dev docs and changelogs.
Citations:
- 1: https://pub.dev/packages/flutter_local_notifications
- 2: https://pub.dev/packages/flutter_local_notifications/versions/16.3.3
- 3: https://pub.dev/packages/flutter_local_notifications/changelog
- 4: https://pub.dev/packages/flutter_local_notifications/versions/20.0.0/changelog
- 5: https://pub.dev/packages/flutter_local_notifications/versions/17.2.4/changelog
🌐 Web query:
flutter_local_notifications AndroidFlutterLocalNotificationsPlugin requestNotificationsPermission API
💡 Result:
The AndroidFlutterLocalNotificationsPlugin class in the flutter_local_notifications package provides the requestNotificationsPermission method to request the POST_NOTIFICATIONS permission on Android 13 (API level 33) and higher. This is necessary for displaying notifications, as the permission is not auto-granted on these versions. Usage example (after plugin initialization): final AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>; final bool? granted = await androidImplementation?.requestNotificationsPermission; Add to AndroidManifest.xml for apps targeting API 33+. The method returns a Future<bool?> indicating if permission was granted. It may trigger app lifecycle changes or direct to settings if previously denied. Note: Older plugin versions used requestPermission (renamed in v16+). For checking status without requesting, use areNotificationsEnabled.
Citations:
- 1: https://stackoverflow.com/questions/75135818/flutter-local-notifications-doesnt-grant-permissions-on-android
- 2: https://stackoverflow.com/questions/77722373/flutter-local-notifications-not-showing
- 3: https://github.com/mahmoodhamdi/Flutter-Local-Notifications-Integration-Guide/blob/main/README.md
- 4: Asking for notification permissions in Android causes lifecycle state change MaikuB/flutter_local_notifications#2363
- 5: https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/AndroidFlutterLocalNotificationsPlugin-class.html
Missing Android 13+ notification permission request.
On Android 13 (API 33+), apps must request the POST_NOTIFICATIONS permission at runtime. The current implementation only handles iOS and will silently fail on newer Android devices.
🔧 Suggested fix
Future<void> requestPermissions() async {
+ // Android 13+ requires runtime permission
+ final androidPlugin = _plugin
+ .resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
+ await androidPlugin?.requestNotificationsPermission;
+
final iosPlugin =
_plugin.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>();
await iosPlugin?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}Also ensure <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> is declared in android/app/src/main/AndroidManifest.xml for apps targeting API 33+.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/services/notification_service.dart` around lines 51 - 60, The
requestPermissions() method only handles iOS—add Android 13+ runtime permission
handling: inside requestPermissions(), detect Android (Platform.isAndroid) and
check SDK_INT >= 33, then request the POST_NOTIFICATIONS permission (e.g., via
the permission_handler package using Permission.notification or
Permission.postNotifications) and handle the granted/denied result before
proceeding; also ensure you declare <uses-permission
android:name="android.permission.POST_NOTIFICATIONS" /> in AndroidManifest.xml
for apps targeting API 33+. Reference the requestPermissions() method and the
_plugin field when locating where to add the Android branch.
| await _plugin.zonedSchedule( | ||
| id, | ||
| title, | ||
| body, | ||
| _nextInstanceOf(hour: hour, minute: minute), | ||
| const NotificationDetails( | ||
| android: AndroidNotificationDetails( | ||
| 'getwelplus_reminders', | ||
| 'Reminders', | ||
| channelDescription: 'Daily wellness and mood reminders', | ||
| importance: Importance.high, | ||
| priority: Priority.high, | ||
| playSound: true, | ||
| ), | ||
| iOS: DarwinNotificationDetails(), | ||
| ), | ||
| payload: payload, | ||
| androidAllowWhileIdle: true, | ||
| uiLocalNotificationDateInterpretation: | ||
| UILocalNotificationDateInterpretation.absoluteTime, | ||
| matchDateTimeComponents: DateTimeComponents.time, | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
flutter_local_notifications zonedSchedule androidScheduleMode parameter
💡 Result:
The androidScheduleMode parameter in the zonedSchedule method of the flutter_local_notifications plugin specifies how Android schedules the notification, balancing timing precision with battery efficiency and permissions. It is a required parameter of type AndroidScheduleMode enum. Usage example from official docs: await flutterLocalNotificationsPlugin.zonedSchedule( id: 0, scheduledDate: scheduledDate, notificationDetails: notificationDetails, androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle, // example value // other params... ); Available AndroidScheduleMode values (from official API docs): - inexact: Roughly at the specified time; may not execute in low-power idle mode (Doze). - inexactAllowWhileIdle: Roughly at the specified time; executes even in low-power idle mode. - exact: Exactly at the specified time; may not execute in low-power idle mode. - exactAllowWhileIdle: Exactly at the specified time; executes even in low-power idle mode. - alarmClock: Exactly at the specified time using AlarmManager's alarm clock feature; works in low-power idle mode. Requires SCHEDULE_EXACT_ALARM permission (Android 12+), which apps must declare as "normal" priority and users must grant explicitly via settings (especially Android 14+). For non-alarm apps, inexactAllowWhileIdle is recommended as it works without special permissions while allowing scheduling when idle. Exact modes may be deferred by Android's power management unless permitted. Additional AndroidManifest.xml receivers are needed for scheduling to work reliably (e.g., ScheduledNotificationReceiver, BootCompletedReceiver, ResumeCompletedReceiver, TzTransitionReceiver).
Citations:
- 1: https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/FlutterLocalNotificationsPlugin/zonedSchedule.html
- 2: https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/AndroidScheduleMode.html
- 3: https://stackoverflow.com/questions/78562615/flutter-local-notifications-zonedschedule-notification-not-firing-on-emulator
- 4: zonedSchedule notifications not sent MaikuB/flutter_local_notifications#2141
- 5: https://stackoverflow.com/questions/79851753/flutter-zoned-scheduled-notifications-work-in-foreground-background-but-unreliab
- 6: Issue with Android 14 MaikuB/flutter_local_notifications#2316
🏁 Script executed:
find . -name "notification_service.dart" -type fRepository: OpenLake/GetWelPlus
Length of output: 115
🏁 Script executed:
find . -name "pubspec.yaml" -type f | head -5Repository: OpenLake/GetWelPlus
Length of output: 89
🏁 Script executed:
# Search for androidAllowWhileIdle usage in the codebase
rg "androidAllowWhileIdle" --type dartRepository: OpenLake/GetWelPlus
Length of output: 148
🏁 Script executed:
cat -n ./flutter_app/pubspec.yaml | grep -A 5 -B 5 "flutter_local_notifications"Repository: OpenLake/GetWelPlus
Length of output: 417
🏁 Script executed:
sed -n '90,130p' ./flutter_app/lib/services/notification_service.dartRepository: OpenLake/GetWelPlus
Length of output: 1115
🌐 Web query:
flutter_local_notifications v13 androidScheduleMode API zonedSchedule
💡 Result:
In flutter_local_notifications version 13 and later, the zonedSchedule method requires an AndroidScheduleMode parameter to control how notifications are scheduled on Android devices. This enum determines the timing precision and behavior under battery optimizations like Doze mode. AndroidScheduleMode values: - inexact: Roughly on time, may not execute while idle (most battery efficient). - inexactAllowWhileIdle: Roughly on time, allows execution while idle. - exact: Exactly on time, but may not execute while idle. - exactAllowWhileIdle: Exactly on time, allows execution while idle (requires SCHEDULE_EXACT_ALARM permission on Android 12+). - alarmClock: Exactly on time using AlarmManager's setAlarmClock (highest priority, requires permission). Usage example: await flutterLocalNotificationsPlugin.zonedSchedule( 0, 'title', 'body', tz.TZDateTime.now(tz.local).add(Duration(seconds: 10)), const NotificationDetails( android: AndroidNotificationDetails('channel_id', 'channel_name'), ), androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, ); For exact timing on modern Android (13+), request exact alarm permission: final androidPlugin = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation; await androidPlugin?.requestExactAlarmsPermission; Add to AndroidManifest.xml for scheduling: Permissions for exact alarms (Android 12+): Note: v13 introduced or stabilized this API; it's required in zonedSchedule for Android scheduling precision control. Choose mode based on timing needs vs. battery impact. Inexact modes don't need special permissions.
Citations:
- 1: https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/FlutterLocalNotificationsPlugin/zonedSchedule.html
- 2: https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/AndroidScheduleMode.html
- 3: https://pub.dev/documentation/flutter_local_notifications/latest
- 4: https://medium.com/@ezsarthak/scheduling-local-notifications-in-flutter-the-right-way-8b99b1a72866
- 5: Schedule notifications not working in Android 14 MaikuB/flutter_local_notifications#2185
- 6: Calling
zonedScheduleon Android 13/Android 14 Beta 2 throwsExactAlarmPermissionExceptionMaikuB/flutter_local_notifications#1995 - 7: https://stackoverflow.com/questions/78562615/flutter-local-notifications-zonedschedule-notification-not-firing-on-emulator
- 8: https://pub.dev/packages/flutter_local_notifications/changelog
- 9: https://pub.dev/packages/flutter_local_notifications/versions/16.3.3/changelog
🏁 Script executed:
sed -n '102,130p' ./flutter_app/lib/services/notification_service.dartRepository: OpenLake/GetWelPlus
Length of output: 888
Add the required androidScheduleMode parameter — missing in v13.0.0+.
The zonedSchedule method requires an androidScheduleMode parameter in flutter_local_notifications v13 and later. The current code omits this required parameter, which is incompatible with the declared dependency version. Replace androidAllowWhileIdle: true with androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle.
🔧 Suggested fix
await _plugin.zonedSchedule(
id,
title,
body,
_nextInstanceOf(hour: hour, minute: minute),
const NotificationDetails(
android: AndroidNotificationDetails(
'getwelplus_reminders',
'Reminders',
channelDescription: 'Daily wellness and mood reminders',
importance: Importance.high,
priority: Priority.high,
playSound: true,
),
iOS: DarwinNotificationDetails(),
),
payload: payload,
- androidAllowWhileIdle: true,
+ androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);📝 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.
| await _plugin.zonedSchedule( | |
| id, | |
| title, | |
| body, | |
| _nextInstanceOf(hour: hour, minute: minute), | |
| const NotificationDetails( | |
| android: AndroidNotificationDetails( | |
| 'getwelplus_reminders', | |
| 'Reminders', | |
| channelDescription: 'Daily wellness and mood reminders', | |
| importance: Importance.high, | |
| priority: Priority.high, | |
| playSound: true, | |
| ), | |
| iOS: DarwinNotificationDetails(), | |
| ), | |
| payload: payload, | |
| androidAllowWhileIdle: true, | |
| uiLocalNotificationDateInterpretation: | |
| UILocalNotificationDateInterpretation.absoluteTime, | |
| matchDateTimeComponents: DateTimeComponents.time, | |
| ); | |
| await _plugin.zonedSchedule( | |
| id, | |
| title, | |
| body, | |
| _nextInstanceOf(hour: hour, minute: minute), | |
| const NotificationDetails( | |
| android: AndroidNotificationDetails( | |
| 'getwelplus_reminders', | |
| 'Reminders', | |
| channelDescription: 'Daily wellness and mood reminders', | |
| importance: Importance.high, | |
| priority: Priority.high, | |
| playSound: true, | |
| ), | |
| iOS: DarwinNotificationDetails(), | |
| ), | |
| payload: payload, | |
| androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, | |
| uiLocalNotificationDateInterpretation: | |
| UILocalNotificationDateInterpretation.absoluteTime, | |
| matchDateTimeComponents: DateTimeComponents.time, | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/services/notification_service.dart` around lines 102 - 123,
The call to _plugin.zonedSchedule is missing the required androidScheduleMode
parameter for flutter_local_notifications v13+; replace the
androidAllowWhileIdle: true argument in the zonedSchedule invocation with
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle so the call
signature matches the newer API and preserves the "allow while idle" behavior
(update the zonedSchedule call in notification_service.dart where
_plugin.zonedSchedule is invoked).
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
flutter_app/lib/services/notification_service.dart (1)
106-127:⚠️ Potential issue | 🟠 MajorReplace deprecated
androidAllowWhileIdlewithandroidScheduleMode.The
androidAllowWhileIdleparameter was replaced byandroidScheduleModeinflutter_local_notificationsv13+. This code will fail to compile with the declared dependency version.🔧 Suggested fix
await _plugin.zonedSchedule( id, title, body, _nextInstanceOf(hour: hour, minute: minute), const NotificationDetails( android: AndroidNotificationDetails( 'getwelplus_reminders', 'Reminders', channelDescription: 'Daily wellness and mood reminders', importance: Importance.high, priority: Priority.high, playSound: true, ), iOS: DarwinNotificationDetails(), ), payload: payload, - androidAllowWhileIdle: true, + androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, matchDateTimeComponents: DateTimeComponents.time, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@flutter_app/lib/services/notification_service.dart` around lines 106 - 127, The zonedSchedule call in _plugin.zonedSchedule uses the removed parameter androidAllowWhileIdle; replace it with the new androidScheduleMode parameter (from flutter_local_notifications) in the same call site and set it to the appropriate enum value (e.g., AndroidScheduleMode.exactAllowWhileIdle or AndroidScheduleMode.inexactAllowWhileIdle depending on desired behavior), and add the necessary import for AndroidScheduleMode; ensure the replacement is done in the zonedSchedule invocation that includes NotificationDetails and matchDateTimeComponents.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@flutter_app/lib/services/notification_service.dart`:
- Around line 27-28: tz.getLocation(timeZoneName) can throw
LocationNotFoundException for unrecognized timezones; wrap the call that sets
the local timezone (the sequence using FlutterNativeTimezone.getLocalTimezone,
tz.getLocation and tz.setLocalLocation) in a try/catch, catch
LocationNotFoundException (and a general Exception as fallback), and on error
fall back to a safe default location (e.g., tz.UTC or tz.local) and log the
error so the app doesn't crash. Ensure you reference
FlutterNativeTimezone.getLocalTimezone, tz.getLocation, tz.setLocalLocation and
the caught LocationNotFoundException when applying the fix.
- Around line 52-64: The Android notification permission call in
requestPermissions() uses the old method name; update the call on the
androidPlugin (resolved as AndroidFlutterLocalNotificationsPlugin) from
requestPermission() to requestNotificationsPermission() so it requests the
POST_NOTIFICATIONS runtime permission on Android 13+; locate the
requestPermissions() method and replace the androidPlugin?.requestPermission()
invocation with androidPlugin?.requestNotificationsPermission().
---
Duplicate comments:
In `@flutter_app/lib/services/notification_service.dart`:
- Around line 106-127: The zonedSchedule call in _plugin.zonedSchedule uses the
removed parameter androidAllowWhileIdle; replace it with the new
androidScheduleMode parameter (from flutter_local_notifications) in the same
call site and set it to the appropriate enum value (e.g.,
AndroidScheduleMode.exactAllowWhileIdle or
AndroidScheduleMode.inexactAllowWhileIdle depending on desired behavior), and
add the necessary import for AndroidScheduleMode; ensure the replacement is done
in the zonedSchedule invocation that includes NotificationDetails and
matchDateTimeComponents.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 85812709-2610-4837-92db-7399568c6ee0
📒 Files selected for processing (2)
flutter_app/android/app/src/main/AndroidManifest.xmlflutter_app/lib/services/notification_service.dart
✅ Files skipped from review due to trivial changes (1)
- flutter_app/android/app/src/main/AndroidManifest.xml
| final String timeZoneName = await FlutterNativeTimezone.getLocalTimezone(); | ||
| tz.setLocalLocation(tz.getLocation(timeZoneName)); |
There was a problem hiding this comment.
Unhandled exception if device timezone is unrecognized.
tz.getLocation(timeZoneName) throws LocationNotFoundException if the timezone string returned by FlutterNativeTimezone.getLocalTimezone() isn't in the timezone database. This can crash the app on devices with unusual timezone configurations.
🛡️ Proposed fix with fallback
tz.initializeTimeZones();
final String timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
- tz.setLocalLocation(tz.getLocation(timeZoneName));
+ try {
+ tz.setLocalLocation(tz.getLocation(timeZoneName));
+ } catch (_) {
+ // Fallback to UTC if device timezone is unrecognized
+ tz.setLocalLocation(tz.UTC);
+ }📝 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.
| final String timeZoneName = await FlutterNativeTimezone.getLocalTimezone(); | |
| tz.setLocalLocation(tz.getLocation(timeZoneName)); | |
| tz.initializeTimeZones(); | |
| final String timeZoneName = await FlutterNativeTimezone.getLocalTimezone(); | |
| try { | |
| tz.setLocalLocation(tz.getLocation(timeZoneName)); | |
| } catch (_) { | |
| // Fallback to UTC if device timezone is unrecognized | |
| tz.setLocalLocation(tz.UTC); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/services/notification_service.dart` around lines 27 - 28,
tz.getLocation(timeZoneName) can throw LocationNotFoundException for
unrecognized timezones; wrap the call that sets the local timezone (the sequence
using FlutterNativeTimezone.getLocalTimezone, tz.getLocation and
tz.setLocalLocation) in a try/catch, catch LocationNotFoundException (and a
general Exception as fallback), and on error fall back to a safe default
location (e.g., tz.UTC or tz.local) and log the error so the app doesn't crash.
Ensure you reference FlutterNativeTimezone.getLocalTimezone, tz.getLocation,
tz.setLocalLocation and the caught LocationNotFoundException when applying the
fix.
| Future<void> requestPermissions() async { | ||
| final androidPlugin = | ||
| _plugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>(); | ||
| await androidPlugin?.requestPermission(); | ||
|
|
||
| final iosPlugin = | ||
| _plugin.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>(); | ||
| await iosPlugin?.requestPermissions( | ||
| alert: true, | ||
| badge: true, | ||
| sound: true, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
flutter_local_notifications AndroidFlutterLocalNotificationsPlugin requestNotificationsPermission vs requestPermission API v13
💡 Result:
In the flutter_local_notifications plugin, for Android (especially API level 33/Android 13+), the AndroidFlutterLocalNotificationsPlugin class provides the requestNotificationsPermission method to request the POST_NOTIFICATIONS runtime permission required to display notifications. The older requestPermission method was deprecated/renamed to requestNotificationsPermission in version 16.0.0 (as seen in related plugin changelogs and GitHub issues where users report 'requestPermission' not defined, with comments confirming the replacement). Usage example: final AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>; await androidImplementation?.requestNotificationsPermission; This shows the system permission dialog on Android 13+. Check status first with areNotificationsEnabled if needed. Also add to AndroidManifest.xml and targetSdkVersion 33+. The plugin's current API docs (v21.0.0 as of 2026) confirm requestNotificationsPermission is not listed but community/official README and examples use it; the class page lists related methods like areNotificationsEnabled.
Citations:
- 1: The method 'requestPermission' isn't defined for the type 'AndroidFlutterLocalNotificationsPlugin'. MaikuB/flutter_local_notifications#1992
- 2: https://stackoverflow.com/questions/77722373/flutter-local-notifications-not-showing
- 3: https://pub.dev/packages/flutter_local_notifications
- 4: https://github.com/mahmoodhamdi/Flutter-Local-Notifications-Integration-Guide
- 5: https://pub.dev/packages/flutter_local_notifications_plus/versions/16.1.0/changelog
- 6: flutterLocalNotificationsPlugin.initialize block app open in release mode MaikuB/flutter_local_notifications#2391
- 7: https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/README.md
- 8: https://pub.dev/packages/flutter_local_notifications/example
- 9: https://medium.com/@abhishekbadar/local-notifications-in-flutter-with-scheduling-a-clean-step-by-step-guide-6d6f4655a152
- 10: https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/AndroidFlutterLocalNotificationsPlugin-class.html
- 11: Asking for notification permissions in Android causes lifecycle state change MaikuB/flutter_local_notifications#2363
🏁 Script executed:
find . -name "pubspec.yaml" -o -name "pubspec.lock" | head -5Repository: OpenLake/GetWelPlus
Length of output: 116
🏁 Script executed:
cat ./flutter_app/pubspec.yaml | grep -A 5 "flutter_local_notifications"Repository: OpenLake/GetWelPlus
Length of output: 214
🌐 Web query:
flutter_local_notifications v13.0.0 AndroidFlutterLocalNotificationsPlugin requestPermission method API
💡 Result:
In flutter_local_notifications version 13.0.0, the AndroidFlutterLocalNotificationsPlugin class provides the requestNotificationsPermission method to request notification permissions on Android, particularly for Android 13 (API 33) and higher where POST_NOTIFICATIONS runtime permission is required. Usage example: final AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation; final bool? granted = await androidImplementation?.requestNotificationsPermission; This method returns a Future<bool?> indicating if permission was granted. Prerequisites: - Target SDK 33+ in android/app/build.gradle (targetSdkVersion 33) - Add to AndroidManifest.xml Note: Earlier versions used requestPermission, but by v13.0.0 it was requestNotificationsPermission. Check permissions first with areNotificationsEnabled to avoid unnecessary requests. The method may trigger app lifecycle changes even if no dialog shows (e.g., permission previously denied).
Citations:
- 1: Asking for notification permissions in Android causes lifecycle state change MaikuB/flutter_local_notifications#2363
- 2: Android 13 notification permissions not running on android 13 MaikuB/flutter_local_notifications#1697
- 3: https://pub.dev/packages/flutter_local_notifications/example
- 4: https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/AndroidFlutterLocalNotificationsPlugin-class.html
- 5: The method 'requestPermission' isn't defined for the type 'AndroidFlutterLocalNotificationsPlugin'. MaikuB/flutter_local_notifications#1992
🏁 Script executed:
cat -n ./flutter_app/lib/services/notification_service.dart | sed -n '52,64p'Repository: OpenLake/GetWelPlus
Length of output: 591
Change requestPermission() to requestNotificationsPermission() for Android 13+ support.
The code calls requestPermission() on line 55, but flutter_local_notifications v13.0.0 uses the method name requestNotificationsPermission(). Update the call to:
await androidPlugin?.requestNotificationsPermission();
This method is required to request the POST_NOTIFICATIONS runtime permission on Android 13+ (API 33+).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/services/notification_service.dart` around lines 52 - 64, The
Android notification permission call in requestPermissions() uses the old method
name; update the call on the androidPlugin (resolved as
AndroidFlutterLocalNotificationsPlugin) from requestPermission() to
requestNotificationsPermission() so it requests the POST_NOTIFICATIONS runtime
permission on Android 13+; locate the requestPermissions() method and replace
the androidPlugin?.requestPermission() invocation with
androidPlugin?.requestNotificationsPermission().
Added local notification scheduling to remind users to log daily mood, and to suggest stress checks / doctor sessions when mood trends low.
Implemented a shared NotificationService with deep-link routing so taps open the correct screen (Mood Tracker, Stress Check, Online Meet).
Improved admin/doctor experience (patient search + clearer selected-patient context in Maya chat).
Added user-facing error handling to avoid showing raw exception text on network issues.
Added maya to assist doctor as an assistant
Summary by CodeRabbit
New Features
Improvements