Files
2026-03-16 20:12:01 +01:00

11 KiB

phase: 04-notifications plan: 01 subsystem: notifications tags: [flutter_local_notifications, timezone, flutter_timezone, shared_preferences, riverpod, android, drift] # Dependency graph requires: - phase: 03-daily-plan provides: DailyPlanDao with tasks/rooms database access - phase: 01-foundation provides: SharedPreferences pattern via ThemeNotifier provides: - NotificationService singleton wrapping FlutterLocalNotificationsPlugin - NotificationSettingsNotifier persisting enabled + TimeOfDay to SharedPreferences - DailyPlanDao one-shot queries for overdue and today task counts - Android build configured for flutter_local_notifications v21 - Timezone initialization in main.dart - 7 notification ARB strings for German locale affects: [04-02-settings-ui, future notification scheduling] # Tech tracking tech-stack: added: [flutter_local_notifications ^21.0.0, timezone ^0.11.0, flutter_timezone ^1.0.8] patterns: [singleton service for native plugin wrapper, @Riverpod(keepAlive) notifier with sync default + async load override] key-files: created: - lib/core/notifications/notification_service.dart - lib/core/notifications/notification_settings_notifier.dart - lib/core/notifications/notification_settings_notifier.g.dart - test/core/notifications/notification_service_test.dart - test/core/notifications/notification_settings_notifier_test.dart modified: - pubspec.yaml - android/app/build.gradle.kts - android/app/src/main/AndroidManifest.xml - lib/main.dart - lib/features/home/data/daily_plan_dao.dart - lib/features/home/data/daily_plan_dao.g.dart - lib/l10n/app_de.arb key-decisions: - "timezone constraint upgraded to ^0.11.0 — flutter_local_notifications v21 requires ^0.11.0, plan specified ^0.9.4" - "flutter_local_notifications v21 uses named parameters in initialize() and zonedSchedule() — updated from positional parameter style in RESEARCH.md examples" - "Generated Riverpod 3 provider named notificationSettingsProvider (not notificationSettingsNotifierProvider) — consistent with existing themeProvider naming convention" - "nextInstanceOf exposed as @visibleForTesting public method (not private _nextInstanceOf) to enable unit testing without mocking" - "Test helper makeContainer() awaits Future.delayed(Duration.zero) to let initial async _load() complete before mutating state assertions" patterns-established: - "Plain Dart singleton for native plugin wrapper: NotificationService uses factory constructor + static _instance, initialized once at app startup outside Riverpod" - "Sync default + async load pattern: @Riverpod(keepAlive: true) returns const default synchronously in build(), async _load() overrides state after SharedPreferences hydration" - "TDD with async state: test helper function awaits initial async load before running mutation tests to avoid race conditions" requirements-completed: [NOTF-01, NOTF-02] # Metrics duration: 9min completed: 2026-03-16

Phase 4 Plan 1: Notification Infrastructure Summary

flutter_local_notifications v21 singleton service with TZ-aware scheduling, Riverpod keepAlive settings notifier persisting to SharedPreferences, Android desugaring config, and DailyPlanDao one-shot task count queries

Performance

  • Duration: 9 min
  • Started: 2026-03-16T13:48:28Z
  • Completed: 2026-03-16T13:57:42Z
  • Tasks: 2
  • Files modified: 11

Accomplishments

  • Android build fully configured for flutter_local_notifications v21: compileSdk=35, core library desugaring enabled, permissions and receivers in AndroidManifest
  • NotificationService singleton wrapping FlutterLocalNotificationsPlugin with initialize, requestPermission, scheduleDailyNotification, cancelAll, and @visibleForTesting nextInstanceOf
  • NotificationSettingsNotifier with @Riverpod(keepAlive: true) persisting enabled/time to SharedPreferences, following ThemeNotifier pattern
  • DailyPlanDao extended with getOverdueAndTodayTaskCount and getOverdueTaskCount one-shot Future queries
  • Timezone initialization chain in main.dart: initializeTimeZones → getLocalTimezone → setLocalLocation → NotificationService.initialize
  • 7 German ARB strings for notification UI and content
  • 12 new unit tests (5 service, 7 notifier) plus all 72 existing tests passing (84 total)

Task Commits

Each task was committed atomically:

  1. Task 1: Android config, packages, NotificationService, timezone init, DAO query, ARB strings - 8787671 (feat)
  2. Task 2 RED: Failing tests for NotificationSettingsNotifier and NotificationService - 0f6789b (test)
  3. Task 2 GREEN: NotificationSettingsNotifier implementation + fixed tests - 4f72eac (feat)

Plan metadata: (docs commit — see final commit hash below)

Note: TDD task 2 has separate test (RED) and implementation (GREEN) commits per TDD protocol

Files Created/Modified

  • lib/core/notifications/notification_service.dart - Singleton wrapping FlutterLocalNotificationsPlugin; scheduleDailyNotification uses zonedSchedule with TZDateTime
  • lib/core/notifications/notification_settings_notifier.dart - @Riverpod(keepAlive: true) notifier; NotificationSettings data class with enabled + time
  • lib/core/notifications/notification_settings_notifier.g.dart - Riverpod code gen; provider named notificationSettingsProvider
  • test/core/notifications/notification_service_test.dart - Unit tests for singleton pattern and nextInstanceOf TZ logic
  • test/core/notifications/notification_settings_notifier_test.dart - Unit tests for default state, setEnabled, setTime, and persistence loading
  • pubspec.yaml - Added flutter_local_notifications ^21.0.0, timezone ^0.11.0, flutter_timezone ^1.0.8
  • android/app/build.gradle.kts - compileSdk=35, isCoreLibraryDesugaringEnabled=true, desugar_jdk_libs:2.1.4 dependency
  • android/app/src/main/AndroidManifest.xml - POST_NOTIFICATIONS + RECEIVE_BOOT_COMPLETED permissions, ScheduledNotificationReceiver + ScheduledNotificationBootReceiver
  • lib/main.dart - async main with timezone init chain and NotificationService.initialize()
  • lib/features/home/data/daily_plan_dao.dart - Added getOverdueAndTodayTaskCount and getOverdueTaskCount one-shot queries
  • lib/l10n/app_de.arb - 7 new keys: settingsSectionNotifications, notificationsEnabledLabel, notificationsTimeLabel, notificationsPermissionDeniedHint, notificationTitle, notificationBody, notificationBodyWithOverdue

Decisions Made

  • timezone version upgraded to ^0.11.0: Plan specified ^0.9.4, but flutter_local_notifications v21 requires ^0.11.0. Auto-fixed (Rule 3 — blocking).
  • v21 named parameter API: RESEARCH.md examples used old positional parameter style. v21 uses settings:, id:, scheduledDate:, notificationDetails: named params. Fixed to match actual API.
  • Riverpod 3 naming convention: Generated provider is notificationSettingsProvider not notificationSettingsNotifierProvider, consistent with existing themeProvider decision from Phase 1.
  • nextInstanceOf public @visibleForTesting: Made public with annotation instead of private _nextInstanceOf to enable unit testing without native dispatch mocking.
  • makeContainer() async helper: Test helper awaits Future.delayed(Duration.zero) after first read to let the async _load() from build() complete before mutation tests run, preventing race conditions.

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] timezone package version constraint incompatible

  • Found during: Task 1 (flutter pub get)
  • Issue: Plan specified timezone: ^0.9.4 but flutter_local_notifications v21 depends on timezone: ^0.11.0 — pub solve failed immediately
  • Fix: Updated constraint to ^0.11.0 in pubspec.yaml
  • Files modified: pubspec.yaml
  • Verification: flutter pub get resolved successfully
  • Committed in: 8787671

2. [Rule 1 - Bug] flutter_local_notifications v21 uses named parameters

  • Found during: Task 2 (first test run against NotificationService)
  • Issue: RESEARCH.md pattern and plan used positional parameters for _plugin.initialize() and _plugin.zonedSchedule(). flutter_local_notifications v21 changed to named parameters — compile error "Too many positional arguments"
  • Fix: Updated NotificationService to use settings:, id:, scheduledDate:, notificationDetails:, androidScheduleMode: named params
  • Files modified: lib/core/notifications/notification_service.dart
  • Verification: dart analyze clean, tests pass
  • Committed in: 4f72eac

3. [Rule 1 - Bug] Riverpod 3 generated provider name is notificationSettingsProvider

  • Found during: Task 2 (test compilation)
  • Issue: Tests referenced notificationSettingsNotifierProvider but Riverpod 3 code gen for NotificationSettingsNotifier produces notificationSettingsProvider — consistent with existing pattern
  • Fix: Updated all test references to use notificationSettingsProvider
  • Files modified: test/core/notifications/notification_settings_notifier_test.dart
  • Verification: Tests compile and pass
  • Committed in: 4f72eac

4. [Rule 1 - Bug] Async _load() race condition in tests

  • Found during: Task 2 (setTime test failure)
  • Issue: setTime(9:30) persisted correctly but state read back as (9:00) because the async _load() from build() ran after setTime, resetting state to SharedPreferences defaults (hour=7, minute=0 since prefs were empty)
  • Fix: Added makeContainer() async helper that awaits Future.delayed(Duration.zero) to let initial _load() complete before mutations
  • Files modified: test/core/notifications/notification_settings_notifier_test.dart
  • Verification: All 7 notifier tests pass consistently
  • Committed in: 4f72eac

Total deviations: 4 auto-fixed (1 blocking dependency, 3 bugs from API mismatch/race condition) Impact on plan: All auto-fixes were necessary for correctness. No scope creep.

Issues Encountered

  • flutter_local_notifications v21 breaking changes (named params, compileSdk requirement) were not fully reflected in RESEARCH.md examples — all caught and fixed during compilation/test runs.

User Setup Required

None — no external service configuration required.

Next Phase Readiness

  • NotificationService and NotificationSettingsNotifier fully implemented and tested
  • Plan 02 can immediately wire notificationSettingsProvider into SettingsScreen
  • notificationSettingsProvider (notificationSettings.dart) exports are ready for import
  • ScheduledNotificationBootReceiver is registered and exported=true for Android 12+
  • Timezone is initialized at app start — no further setup needed for Plan 02

Phase: 04-notifications Completed: 2026-03-16

Self-Check: PASSED

  • lib/core/notifications/notification_service.dart: FOUND
  • lib/core/notifications/notification_settings_notifier.dart: FOUND
  • lib/core/notifications/notification_settings_notifier.g.dart: FOUND
  • test/core/notifications/notification_service_test.dart: FOUND
  • test/core/notifications/notification_settings_notifier_test.dart: FOUND
  • .planning/phases/04-notifications/04-01-SUMMARY.md: FOUND
  • commit 8787671: FOUND
  • commit 0f6789b: FOUND
  • commit 4f72eac: FOUND