flutter-local-notifications-production-engineering
Skill8 starsChanged 49 days agoMITView on GitHub
Design, implement, review, debug, test, optimize, and migrate production Flutter notification systems with flutter_local_notifications. Use this skill whenever work involves notification permissions, Android channels, scheduled or exact alarms, timezone/DST recurrence, notification IDs or payloads, cold-start/tap routing, background actions, reboot recovery, reconciliation, cross-platform support, Web/Windows/Linux limitations, or deciding between local notifications, push, and background execution—even for a simple schedule-a-notification request.
In this repository70 other files it gives its agents.
- camerawesome-flutterSkill
- dart-args-production-cliSkill
- dart-flutter-equatable-engineeringSkill
- dart-flutter-ffi-systems-engineeringSkill
- dart-metaSkill
- document-camera-frameSkill
- document-scan-flutterSkill
- figma-to-flutter-expertSkill
- flutter-animations-production-engineeringSkill
- flutter-app-hardening-reverse-engineeringSkill
- flutter-app-icon-engineeringSkill
- flutter-application-security-engineeringSkill
- flutter-app-links-deep-link-engineeringSkill
- flutter-cachingSkill
- flutter-concurrency-memory-performanceSkill
- flutter-confetti-engineeringSkill
- flutter-dart-package-engineeringSkill
- flutter-design-system-from-design-mdSkill
- flutter-dio-cache-interceptor-engineeringSkill
- flutter-dio-engineeringSkill
- flutter-doc-scannerSkill
- flutter-drift-persistence-engineeringSkill
- flutter-expert-tipsSkill
- flutter-firebase-analyticsSkill
- flutter-firebase-core-engineeringSkill
- flutter-firebase-messagingSkill
- flutter-get-it-injectable-dependency-injectionSkill
- flutter-go-router-engineeringSkill
- flutter-hive-persistence-engineeringSkill
- flutter-image-performanceSkill
- flutter-image-picker-production-engineeringSkill
- flutter-lints-static-analysis-engineeringSkill
- flutter-memory-leak-engineeringSkill
- flutter-mobile-developmentSkill
- flutter-motion-engineeringSkill
- flutter-multi-environmentSkill
- flutter-native-splash-engineeringSkill
- flutter-package-plugin-selectionSkill
- flutter-path-provider-production-storageSkill
- flutter-performance-jank-optimizationSkill
- flutter-permission-handler-engineeringSkill
- flutter-pinputSkill
- flutter-production-build-releaseSkill
- flutter-production-engineeringSkill
- flutter-production-loggingSkill
- flutter-pro-image-editor-engineeringSkill
- flutter-secure-storage-security-engineeringSkill
- flutter-sentry-engineeringSkill
- flutter-share-plus-engineeringSkill
- flutter-skeletonizer-engineeringSkill
- flutter-squadron-concurrency-engineeringSkill
- flutter-supabase-engineeringSkill
- flutter-svg-engineeringSkill
- flutter-ui-performance-engineeringSkill
- flutter-url-launcher-engineeringSkill
- flutter-video-player-production-engineeringSkill
- flutter-webview-production-engineeringSkill
- freezed-production-engineeringSkill
- html-to-flutterSkill
- hydrated-blocSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
42 rules. About 0 are common, 30 are distinctive, and 12 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈30
Distinctive · estimate
This author's own choices. Worth a look.
12
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 8
Treat flutter_local_notifications as a platform-notification integration boundary, not a business scheduler, durable job engine, or proof …
- Distinctive: line 53
Never use notification delivery to enforce payment, expiry, revocation, synchronization, authorization, or another critical transition.
- Distinctive: line 70
Do not introduce obsolete patterns:
- Distinctive: line 80
Do not force an older repository onto the source baseline merely to make snippets compile.
- Distinctive: line 84
Do not initialize per notification or bootstrap unrelated application services just because notification setup runs early.
- Distinctive: line 95
Do not prompt every startup.
- Distinctive: line 116
Use stable semantic IDs:
- Very specific: line 122
Do not create channels per entity.
- Distinctive: line 124
Do not delete/recreate channels to bypass user preferences.
- Distinctive: line 127
Do not make ordinary reminders, marketing, or informational notifications high/max importance merely to force heads-up behavior.
- Distinctive: line 138
Do not request exact-alarm access for convenience.
- Distinctive: line 139
Before an exact mode, check current capability and provide an inexact fallback when semantics allow it.
- Very specific: line 143
Do not copy the example app manifest wholesale.
- Very specific: line 144
Add only feature-specific items actually used, such as scheduled receivers, boot receiver/RECEIVE_BOOT_COMPLETED, action receiver, …
- Distinctive: line 145
Do not add both exact-alarm permission paths thoughtlessly.
- Distinctive: line 152
Use stable machine IDs and localized labels:
- Very specific: line 158
Use @pragma('vm:entry-point') for native-invoked background Dart entry points where required.
- Very specific: line 160
Do not use BuildContext, navigation, Activity-bound plugins, broad app bootstrap, migrations, or long workflows from a background isolate.
- Distinctive: line 161
If UI must open, foreground the app and hand off to normal lifecycle routing.
- Distinctive: line 164
Do not pretend every platform supports the same features.
- Distinctive: line 182
Never silently convert between them.
- Very specific: line 192
Test DST spring-forward gaps, fall-back folds, timezone changes, day/month/year boundaries, leap years, and edits near firing time.
- Distinctive: line 195
Use zoned/calendar recurrence for daily local time, weekly weekday/time, monthly, or yearly schedules.
- Distinctive: line 196
Do not assume periodic APIs are portable to Windows, Linux, or Web.
- Distinctive: line 200
Prefer fixed reserved IDs for singleton notifications, persistent domain/UUID→32-bit mappings, stable IDs for recurring reminders, and …
- Very specific: line 201
Avoid timestamps, randomness, and runtime hashCode when identity must survive restarts/builds.
- Distinctive: line 218
Never store tokens, passwords, API keys, sessions, authorization claims, or unnecessary PII in payloads.
- Very specific: line 230
Use getNotificationAppLaunchDetails() for cold-start launch detection where applicable.
- Very specific: line 231
Do not navigate during pre-runApp() initialization or use global BuildContext hacks.
- Distinctive: line 242
Do not reschedule unchanged reminders.
- Distinctive: line 257
Do not synchronously fetch/decode large assets on a latency-critical path.
- Very specific: line 269
Do not spread AndroidNotificationDetails, DarwinNotificationDetails, or plugin instances through presentation state.
- Very specific: line 290
Do not micro-optimize tiny NotificationDetails construction while wasting battery with unnecessary exact alarms.
- Distinctive: line 311
Do not write tests that merely prove plugin internals.
- Distinctive: line 315
Test API 33+ permission grant/deny, disabled notifications, exact access when used, inexact/exact behavior, Doze/idle, reboot, app update, …
- Distinctive: line 317
Test permissions, foreground presentation, cold-start activation, actions, timezone/DST, and actual callback lifecycle.
- Distinctive: line 319
Test only the supported subset and actual shipped packaging/browser environments.
- Distinctive: line 334
Before a major upgrade:
- Distinctive: line 336
read changelog and target docs.
- Distinctive: line 343
test existing Android channels on an already-installed app.
- Distinctive: line 344
test persisted reminders/schedules.
- Distinctive: line 346
test coexistence with push infrastructure.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Flutter Local Notifications Production Engineering
Treat flutter_local_notifications as a platform-notification integration boundary, not a business scheduler, durable job engine, or proof that business logic executed.
Use this model:
durable business notification intent
→ platform capability + permission policy
→ stable notification identity + payload
→ OS scheduling/presentation
→ lifecycle response
→ validated application intent
→ authoritative auth/data check
→ route/actionReference routing
| Need | Read |
| Version/API/initialization/package architecture/build baseline | references/01-foundations-api-initialization.md |
| Android channels/permissions/alarms/reboot/actions; iOS/macOS/Linux/Windows/Web | references/02-platform-integrations.md |
| Timezone/DST, IDs, payloads, routing, reconciliation, architecture, security/performance | references/03-scheduling-routing-security.md |
| Testing, devices, diagnostics, errors, bugs, anti-patterns | references/04-testing-diagnostics-antipatterns.md |
| Full code examples, native config, migration, push coexistence, release checklists | references/05-code-migration-release.md |
Consult the relevant reference before making version- or platform-sensitive changes.
1. Classify the requirement first
Answer before coding:
Where does the event originate?
Notification or actual execution?
Immediate or scheduled?
One-time or recurring?
Wall-clock or elapsed recurrence?
Approximate or genuinely exact?
Must Android reboot recovery work?
Can activation occur from a terminated app?
What if permission is denied?
Is displayed content sensitive?Choose:
locally known reminder/timer/event → local notification
server-originated event → push notification
business/background execution → background task/service/server jobNever use notification delivery to enforce payment, expiry, revocation, synchronization, authorization, or another critical transition.
2. Inspect the repository
Inspect pubspec.yaml, pubspec.lock, SDK constraints, installed plugin version, Android manifest/build config, Apple lifecycle/deployment targets, Windows identity, Linux/Web integration, router/auth, persistence, background packages, push packages, timezone setup, DI/state management, and tests.
Search for:
FlutterLocalNotificationsPlugin initialize( show( zonedSchedule(
periodicallyShow( pendingNotificationRequests( getNotificationAppLaunchDetails(
cancel( AndroidNotificationChannel AndroidScheduleMode
SCHEDULE_EXACT_ALARM USE_EXACT_ALARM POST_NOTIFICATIONS
ScheduledNotificationReceiver ScheduledNotificationBootReceiver
ActionBroadcastReceiver @pragma('vm:entry-point') TZDateTime NotificationResponseFlag duplicate initialization, deprecated APIs, random IDs, dynamic channels, naive recurrence, payload trust, permission spam, stale schedules, and exact alarms used by default.
3. Version-gate syntax and native requirements
The supplied baseline is flutter_local_notifications 22.3.0. Modern 22.x code uses named-parameter APIs.
Do not introduce obsolete patterns:
schedule(...)
showDailyAtTime(...)
showWeeklyAtDayAndTime(...)
Time(...)
androidAllowWhileIdle
onDidReceiveLocalNotification
old positional signaturesDo not force an older repository onto the source baseline merely to make snippets compile. Preserve newer compatible native toolchains rather than downgrading them to examples.
4. Initialize once
Initialize at one intentional application lifecycle boundary. If plugin work runs before runApp(), initialize Flutter bindings first. Await initialization when later logic depends on categories, callbacks, launch details, or scheduling.
Do not initialize per notification or bootstrap unrelated application services just because notification setup runs early.
5. Request permissions contextually
Preferred UX:
user enables notification feature
→ explain value
→ request system permission
→ granted: enable delivery
→ denied: preserve coherent product state and expose delivery limitationDo not prompt every startup. Permission denial is normal user state, not corruption. On Web, prompt only from a direct user gesture. On Apple platforms, avoid automatic initialization-time prompts unless intentional.
6. Persist business intent separately from OS state
The reminder database, not the OS notification queue, is authoritative. Persist application-level intent such as:
logical reminder ID
enabled
schedule type
UTC instant or local calendar components
IANA timezone ID
recurrence
stable platform notification ID
payload entity reference
schedule schema/revision
last delivery capability/errorThis enables recovery after permissions, timezone changes, upgrades, reboot, schedule drift, or platform resets.
7. Treat Android channel IDs as product contracts
Use stable semantic IDs:
reminders.v1
messages.v1
downloads.v1Do not create channels per entity.
Once created, changing importance/sound/vibration in Dart does not reliably rewrite an installed user's channel. If behavior must materially change, use deliberate channel versioning such as reminders.v2.
Do not delete/recreate channels to bypass user preferences.
8. Match importance to real urgency
Do not make ordinary reminders, marketing, or informational notifications high/max importance merely to force heads-up behavior. Use interruption levels users would reasonably expect. Remember users can override channel behavior.
9. Use the least-powerful Android scheduling mode
Default policy:
inexact
→ inexactAllowWhileIdle only when idle delivery matters
→ exact only for genuine precise timing
→ exactAllowWhileIdle only for precise idle timing
→ alarmClock only for actual alarm-clock semanticsDo not request exact-alarm access for convenience. If approximate timing satisfies the feature, keep it approximate so Android can batch wakeups. Before an exact mode, check current capability and provide an inexact fallback when semantics allow it. Exact alarms are not guaranteed business execution.
10. Add only required Android manifest capabilities
Do not copy the example app manifest wholesale.
Add only feature-specific items actually used, such as scheduled receivers, boot receiver/RECEIVE_BOOT_COMPLETED, action receiver, justified exact-alarm permission, full-screen intent for legitimate alarm/call use, foreground-service declarations for real foreground work, or DND policy access when truly required.
Do not add both exact-alarm permission paths thoughtlessly.
11. Model reboot and force-stop honestly
Configure documented Android receivers if schedules must survive reboot/update. Do not promise normal delivery after user force-stop; reconcile durable reminder intent after explicit relaunch. Uninstall/data clearing destroys local state. Reconstruct schedules from authoritative data where product architecture permits.
12. Keep actions stable and background callbacks small
Use stable machine IDs and localized labels:
category: reminder.v1
action: reminder.open
action: reminder.snoozeUse @pragma('vm:entry-point') for native-invoked background Dart entry points where required.
Background callbacks should parse/validate, perform only small safe work, record diagnostics, and return.
Do not use BuildContext, navigation, Activity-bound plugins, broad app bootstrap, migrations, or long workflows from a background isolate.
If UI must open, foreground the app and hand off to normal lifecycle routing.
13. Preserve platform differences
Do not pretend every platform supports the same features. Important supplied constraints:
Linux → no plugin scheduled/pending notifications
Windows→ one-time schedule supported; repeating plugin schedules unsupported
Web → immediate notifications supported; scheduled/repeating unsupported
macOS → do not assume Android/iOS background-isolate callback behaviorReject unsupported operations before plugin invocation when possible. Do not fake Web/Linux scheduling with a long-lived Flutter timer/process.
14. Model time semantics explicitly
Classify each schedule:
absolute event → UTC instant
one local calendar event → local components + named timezone
daily at 08:00 → named zone + calendar recurrence
every 24 elapsed hours → duration/intervalNever silently convert between them. This is wrong for “daily at 08:00”:
previous.add(const Duration(hours: 24));Across DST, elapsed and wall-clock time differ.
15. Use named time zones for wall-clock schedules
Initialize timezone data before zoned scheduling. Persist IANA zones such as Europe/Tallinn; do not rely on an ambiguous local DateTime when future rules matter.
The timezone package does not discover the device zone itself; use the repository's platform mechanism or a maintained dependency.
Test DST spring-forward gaps, fall-back folds, timezone changes, day/month/year boundaries, leap years, and edits near firing time. Define product policy for ambiguous/nonexistent local times.
16. Use calendar recurrence for calendar intent
Use zoned/calendar recurrence for daily local time, weekly weekday/time, monthly, or yearly schedules. Use interval APIs only for true elapsed-time recurrence. Do not assume periodic APIs are portable to Windows, Linux, or Web.
17. Use deterministic notification IDs
IDs must allow later replacement/cancellation.
Prefer fixed reserved IDs for singleton notifications, persistent domain/UUID→32-bit mappings, stable IDs for recurring reminders, and distinct reserved namespaces for summaries.
Avoid timestamps, randomness, and runtime hashCode when identity must survive restarts/builds.
Persist generated mappings when the domain ID is not already a safe stable integer.
18. Keep payloads small, versioned, and untrusted
Payloads are routing references, not domain snapshots:
{"v":1,"type":"reminder","id":"8f0e..."}On activation:
- parse defensively;
- allowlist schema version/type;
- validate identifier format;
- re-read authoritative data;
- verify entity existence;
- verify current authentication;
- verify current authorization;
- route only after checks. Never store tokens, passwords, API keys, sessions, authorization claims, or unnecessary PII in payloads.
19. Centralize response routing
Prefer:
NotificationResponse
→ NotificationPayloadParser
→ NotificationIntent
→ NotificationRouteCoordinator
→ routerSupport foreground/warm response, background→foreground response, cold-start launch, and deferred routing until router/auth readiness.
Use getNotificationAppLaunchDetails() for cold-start launch detection where applicable.
Do not navigate during pre-runApp() initialization or use global BuildContext hacks.
20. Reconcile from durable intent
Use event-driven reconciliation:
create/enable → schedule
edit/move → cancel old + schedule desired
delete/disable → cancel
permission/exact access/timezone restored → reconcile affected reminders
schema/algorithm upgrade → deliberate migration/reconciliationDo not reschedule unchanged reminders. Do not reconcile from widget builds. For large future sets, schedule a bounded/rolling window when semantics allow; platform pending queues are not unlimited storage.
21. Cancel/update intentionally
Distinguish cancel-one, cancel-pending, and remove-all semantics. Do not use cancelAll() unless the product truly means remove every delivered and pending notification owned by this integration.
For portable pending replacement, prefer:
stable logical ID
→ cancel old schedule
→ schedule desired state22. Keep rich media/progress bounded
Rich notifications may involve network, disk, decode, attachments, and cleanup. The plugin is not a media downloader/cache. Application code should own timeouts, bounded response size, caching, fallback, and temporary-file cleanup. Do not synchronously fetch/decode large assets on a latency-critical path. For progress, update one stable ID and throttle by meaningful time/percentage thresholds—not every byte/chunk.
23. Keep plugin types at the integration boundary
UI/state should model product concepts:
reminderEnabled
schedule
notificationPermission
deliveryCapability
lastSchedulingFailureDo not spread AndroidNotificationDetails, DarwinNotificationDetails, or plugin instances through presentation state.
A medium app may use:
NotificationService plugin/native integration
ReminderScheduler business→platform schedule mapping
NotificationPayloadParser routing envelope validation
NotificationRouteCoordinator lifecycle/router handoffA tiny app may need one small service. Avoid forwarding-layer ceremony.
24. Optimize mechanisms that actually cost resources
Prioritize:
- exact alarms and idle wakeups
- huge future schedule sets
- repeated pending/active queries
- repeated full reconciliation
- repeated timezone setup
- background Flutter-engine startup
- media download/decode
- excessive progress updates
- oversized payloads
Do not micro-optimize tiny
NotificationDetailsconstruction while wasting battery with unnecessary exact alarms. At startup, do only what lifecycle correctness requires.
25. Treat notification activation as a security entry point
Threat-model malformed/stale payloads, deleted resources, logout/account changes, authorization changes, deep-link abuse, stale actions, lock-screen disclosure, and sensitive logs. Payload data is never authorization. Re-check current application rules before exposing protected content or executing actions. Review notification bodies for lock-screen privacy and prefer generic secure wording when content is sensitive.
26. Test application policy separately from OS behavior
Unit-test application-owned logic:
- next-occurrence calculation
- UTC/local conversion
- DST cases
- ID allocation
- payload parsing and malformed rejection
- schedule-mode policy
- reconciliation diff
- permission transitions
- action routing Integration-test expected plugin calls and routing handoff. Do not write tests that merely prove plugin internals.
27. Validate lifecycle on real devices
Android
Test API 33+ permission grant/deny, disabled notifications, exact access when used, inexact/exact behavior, Doze/idle, reboot, app update, force-stop + explicit relaunch, terminated tap, foreground/background actions, channel migration on an existing install, and release icons/sounds.
Apple
Test permissions, foreground presentation, cold-start activation, actions, timezone/DST, and actual callback lifecycle.
Windows/Linux/Web
Test only the supported subset and actual shipped packaging/browser environments.
28. Classify errors instead of swallowing them
Use categories:
expected user state
recoverable platform limitation
configuration defect
programming defect
unexpected runtime failureExamples: permission denied = user state; Web scheduling = limitation; missing receiver/icon = configuration; ID collision/invalid timezone = programming defect; PlatformException = runtime failure.
Log safe metadata such as notification type, logical/platform IDs, schedule kind, Android mode, UTC instant, zone ID, recurrence, platform, capability state, and error code. Do not log sensitive bodies/payloads.
29. Migration workflow
Before a major upgrade:
- inspect installed/target versions;
- read changelog and target docs;
- audit deprecated calls/signatures;
- audit Android manifest and build requirements;
- audit deployment targets;
- inspect callback/background semantics;
- inspect scheduling/exact-alarm semantics;
- inspect platform implementation changes;
- test existing Android channels on an already-installed app;
- test persisted reminders/schedules;
- test cold-start routing;
- test coexistence with push infrastructure. Preserve stable channel IDs, notification IDs, schedule semantics, and routing contracts during migration.
30. Existing-code audit gates
Flag immediately:
- deprecated
schedule()or removed daily/weekly helpers - positional API syntax in a modern repository
- timezone data/device zone never initialized
- UTC/local confusion
- 24-hour arithmetic for wall-clock recurrence
- exact alarms as default
- missing exact capability handling
- missing Android notification-permission UX
- same channel ID reused after intended sound/importance change
- dynamic per-entity channels
- invalid status icon
- missing schedule/boot receiver where required
- missing
@pragma('vm:entry-point') BuildContext/Activity-only plugin in background callback- huge or sensitive payloads
- payload used as authorization
- random IDs/collisions
- reminder edits/deletes leaving stale schedules
- thousands of occurrences eagerly scheduled
- pending queries from widget builds
- repeated plugin initialization
- startup permission spam
- routing before router/auth readiness
- foreground Apple presentation assumed automatic
- periodic support assumed portable
- notification trigger used as business execution
Production definition of done
Approve only when:
- installed plugin/API compatibility is verified;
- platform limitations are explicit;
- initialization occurs once;
- permission prompting is contextual and denial graceful;
- Android channels are stable product contracts;
- channel migrations are deliberate;
- notification IDs are deterministic;
- exact alarms are avoided or justified;
- exact access is handled when needed;
- timezone data and named zones are correct;
- DST semantics match product intent;
- Android reboot recovery is configured where required;
- force-stop behavior is not overstated;
- foreground/background/cold-start interaction is handled;
- background callbacks are small and UI-independent;
- durable reminder intent is separate from OS schedules;
- edits/deletes cancel or replace schedules;
- stale schedules can be reconciled;
- payloads are small, versioned, non-secret, and validated;
- activation re-checks current auth/access;
- unsupported platform features fail explicitly;
- schedule volume is bounded;
- platform queries/rescheduling are not wasteful;
- release notification resources work;
- automated tests pass;
- real-device lifecycle/timezone tests pass.
Final rule
Before implementing, answer:
What causes the event?
Notification or actual background execution?
Absolute, wall-clock, or elapsed timing?
How exact must it really be?
What durable data is authoritative?
What permissions/platform capabilities are needed?
What stable channel/notification IDs represent it?
What happens on reboot, force-stop, logout, timezone change, and upgrade?
What does a tap/action mean after state changes?
What private information reaches the lock screen?Then derive the smallest reliable platform configuration from durable application intent.
Notification delivery is a user-facing signal; it is never proof that business logic executed.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

