flet-notifications 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. flet_notifications-0.1.0/LICENSE +21 -0
  2. flet_notifications-0.1.0/PKG-INFO +144 -0
  3. flet_notifications-0.1.0/README.md +120 -0
  4. flet_notifications-0.1.0/pyproject.toml +56 -0
  5. flet_notifications-0.1.0/setup.cfg +4 -0
  6. flet_notifications-0.1.0/src/flet_notifications/__init__.py +91 -0
  7. flet_notifications-0.1.0/src/flet_notifications/actions.py +55 -0
  8. flet_notifications-0.1.0/src/flet_notifications/base.py +87 -0
  9. flet_notifications-0.1.0/src/flet_notifications/channels.py +87 -0
  10. flet_notifications-0.1.0/src/flet_notifications/events.py +43 -0
  11. flet_notifications-0.1.0/src/flet_notifications/local_notifications.py +613 -0
  12. flet_notifications-0.1.0/src/flet_notifications/options.py +358 -0
  13. flet_notifications-0.1.0/src/flet_notifications/patcher.py +283 -0
  14. flet_notifications-0.1.0/src/flet_notifications/scheduling.py +123 -0
  15. flet_notifications-0.1.0/src/flet_notifications/styles.py +160 -0
  16. flet_notifications-0.1.0/src/flet_notifications/types.py +53 -0
  17. flet_notifications-0.1.0/src/flet_notifications.egg-info/PKG-INFO +144 -0
  18. flet_notifications-0.1.0/src/flet_notifications.egg-info/SOURCES.txt +36 -0
  19. flet_notifications-0.1.0/src/flet_notifications.egg-info/dependency_links.txt +1 -0
  20. flet_notifications-0.1.0/src/flet_notifications.egg-info/entry_points.txt +2 -0
  21. flet_notifications-0.1.0/src/flet_notifications.egg-info/requires.txt +1 -0
  22. flet_notifications-0.1.0/src/flet_notifications.egg-info/top_level.txt +2 -0
  23. flet_notifications-0.1.0/src/flutter/flet_notifications/lib/flet_notifications.dart +3 -0
  24. flet_notifications-0.1.0/src/flutter/flet_notifications/lib/src/background_handler.dart +106 -0
  25. flet_notifications-0.1.0/src/flutter/flet_notifications/lib/src/extension.dart +27 -0
  26. flet_notifications-0.1.0/src/flutter/flet_notifications/lib/src/notifications_service.dart +1386 -0
  27. flet_notifications-0.1.0/src/flutter/flet_notifications/lib/src/parsers.dart +303 -0
  28. flet_notifications-0.1.0/src/flutter/flet_notifications/lib/src/scheduling.dart +106 -0
  29. flet_notifications-0.1.0/src/flutter/flet_notifications/pubspec.yaml +24 -0
  30. flet_notifications-0.1.0/tests/test_actions.py +96 -0
  31. flet_notifications-0.1.0/tests/test_channels.py +250 -0
  32. flet_notifications-0.1.0/tests/test_events.py +180 -0
  33. flet_notifications-0.1.0/tests/test_options.py +181 -0
  34. flet_notifications-0.1.0/tests/test_patcher.py +268 -0
  35. flet_notifications-0.1.0/tests/test_probes.py +256 -0
  36. flet_notifications-0.1.0/tests/test_scheduling.py +281 -0
  37. flet_notifications-0.1.0/tests/test_service.py +177 -0
  38. flet_notifications-0.1.0/tests/test_styles.py +108 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Onyeka Nwokike
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: flet-notifications
3
+ Version: 0.1.0
4
+ Summary: Cross-platform local notifications for Flet apps (Android, iOS, macOS, Windows, Linux, Web).
5
+ Author-email: Onyeka Nwokike <nwokikeonyeka@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Nwokike/flet-notifications
8
+ Project-URL: Repository, https://github.com/Nwokike/flet-notifications
9
+ Project-URL: Issues, https://github.com/Nwokike/flet-notifications/issues
10
+ Keywords: flet,notifications,desktop,android,ios,cross-platform
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Intended Audience :: Developers
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: flet>=1.0.0
23
+ Dynamic: license-file
24
+
25
+ # flet-notifications
26
+
27
+ [![pypi](https://img.shields.io/pypi/v/flet-notifications.svg)](https://pypi.python.org/pypi/flet-notifications)
28
+ [![python](https://img.shields.io/badge/python-%3E%3D3.10-%2334D058)](https://pypi.org/project/flet-notifications)
29
+ [![license](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/Nwokike/flet-notifications/blob/main/LICENSE)
30
+
31
+ Cross-platform local notifications for [Flet](https://flet.dev) apps.
32
+
33
+ A 1:1 wrapping of the official [flutter_local_notifications](https://pub.dev/packages/flutter_local_notifications) Flutter plugin, in the same architecture as [flet-ads](https://pypi.org/project/flet-ads/). One `uv add flet-notifications` covers Android, iOS, macOS, Windows, Linux and Web โ€” no Python dependencies beyond `flet`.
34
+
35
+ ## Test it without an account
36
+
37
+ Every release builds the example app for every platform. Sideload any of them and every button is live:
38
+
39
+ | Variant | Download | Notes |
40
+ | :--- | :---: | :--- |
41
+ | ๐Ÿ“ฑ **ARM64** (most phones/TVs) | [**app-arm64-v8a-release.apk**](https://github.com/Nwokike/flet-notifications/releases/latest/download/app-arm64-v8a-release.apk) | Modern 64-bit Android devices โ€” grab this one unless you know otherwise |
42
+ | ๐Ÿ“ฑ **ARMv7** (older phones/TV boxes) | [**app-armeabi-v7a-release.apk**](https://github.com/Nwokike/flet-notifications/releases/latest/download/app-armeabi-v7a-release.apk) | Legacy 32-bit Android devices |
43
+ | ๐Ÿ’ป **x86_64** (emulators/ChromeOS) | [**app-x86_64-release.apk**](https://github.com/Nwokike/flet-notifications/releases/latest/download/app-x86_64-release.apk) | Android emulators & Chromebooks |
44
+ | ๐Ÿ–ฅ๏ธ **Windows** | [**flet-notifications-example-windows.zip**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-windows.zip) | Portable zip |
45
+ | ๐ŸŽ **macOS** | [**flet-notifications-example-macos.dmg**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-macos.dmg) | Disk image |
46
+ | ๐Ÿง **Linux** | [**tar.gz**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-linux-x86_64.tar.gz) ยท [**deb**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example_amd64.deb) ยท [**rpm**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example_x86_64.rpm) | Bundle or packages |
47
+ | ๐ŸŒ **Web** | [**flet-notifications-example-web.tar.gz**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-web.tar.gz) | Static bundle |
48
+
49
+ Plain `flet run` launches the prebuilt client without the extension's Dart half โ€” notifications need a built app (an artifact above, or `flet build <platform>`).
50
+
51
+ ## Platform Support
52
+
53
+ | Platform | Show | Schedule | Actions & callbacks | Channels | Progress | Notes |
54
+ |----------|:----:|:--------:|:-------------------:|:--------:|:--------:|-------|
55
+ | iOS | โœ… | โœ… | โœ… | โ€” | โ€” | 13+; attachments, interruption levels |
56
+ | Android | โœ… | โœ… | โœ… | โœ… | โœ… | 7.0+ (API 24); Android TV works |
57
+ | macOS | โœ… | โœ… | โœ… | โ€” | โ€” | 10.15+ |
58
+ | Windows | โœ… | one-shot | โœ… | โ€” | โœ… | 10+; `cancel`/queries need MSIX identity |
59
+ | Linux | โœ… | โ€” | โœ… | โ€” | โ€” | D-Bus server; capabilities vary |
60
+ | Web | โœ… | โ€” | Chrome/Edge only | โ€” | โ€” | Notifications API; gesture-gated permissions |
61
+
62
+ ## Usage
63
+
64
+ ```bash
65
+ uv add flet-notifications
66
+ ```
67
+
68
+ ```python
69
+ import flet as ft
70
+ from flet_notifications import LocalNotifications
71
+
72
+ def main(page: ft.Page):
73
+ notifications = LocalNotifications()
74
+
75
+ async def on_tap(e):
76
+ print("tapped:", e.payload)
77
+
78
+ notifications.on_tap = on_tap
79
+
80
+ if await notifications.request_permissions():
81
+ await notifications.show(id=1, title="Hello", body="Hello!")
82
+
83
+ ft.run(main)
84
+ ```
85
+
86
+ ## Controls
87
+
88
+ | Control | Base | Purpose |
89
+ |---|---|---|
90
+ | `LocalNotifications` | `ft.Service` | Show/schedule/cancel, permissions, probes, channels; construct once, it registers itself |
91
+
92
+ ### Event handlers
93
+
94
+ ```python
95
+ notifications.on_tap = on_tap # typed: NotificationEvent
96
+ notifications.on_action = on_action # typed: e.action_id, e.input, e.data
97
+ notifications.on_dismissed = handler # typed
98
+ ```
99
+
100
+ Typed handlers must use `ft.EventHandler[NotificationEvent]`, **not** `ft.ControlEventHandler[NotificationEvent]` โ€” `ControlEventHandler[X]` double-wraps `X` in `Event[...]`, so `e.payload` raises `AttributeError`. `NotificationEvent` carries `id`, `action_id`, `payload`, `input`, `data` and `response_type`.
101
+
102
+ Taps that arrive while the app wasn't running are replayed on the next start (Android background taps, iOS/macOS cold-start launches).
103
+
104
+ ### Configuration
105
+
106
+ Scheduling on Android needs permissions in the app's `pyproject.toml`:
107
+
108
+ ```toml
109
+ [tool.flet.android.permission]
110
+ RECEIVE_BOOT_COMPLETED = true # restore alarms after reboot
111
+ SCHEDULE_EXACT_ALARM = true # exact schedule modes
112
+ ```
113
+
114
+ The scheduled-notification receivers are `<application>` children, which `[tool.flet.android.*]` cannot declare โ€” the `flet-notifications-patch` console script injects them (plus the core-library-desugaring Gradle flags the plugin needs), idempotently:
115
+
116
+ ```bash
117
+ uv run flet build apk --yes --project myapp --org com.example -v
118
+ uv run flet-notifications-patch --project-root build/flutter
119
+ cd build/flutter/android && ./gradlew assembleRelease -Psplit-per-abi=true
120
+ ```
121
+
122
+ ## Configuration reference
123
+
124
+ **Types:** `NotificationAction`/`NotificationActionInput` (buttons, inline reply) ยท `BigTextStyle`/`BigPictureStyle`/`InboxStyle`/`MessagingStyle` (+ `Person`, `Message`, `AndroidIcon`) ยท `NotificationChannel` (importance, sound, vibration, badge, DND, lights) ยท `NotificationOptions` with `AndroidOptions`/`DarwinOptions`/`LinuxOptions`/`WindowsOptions`/`WebOptions` (the full per-platform detail surface) ยท enums `RepeatInterval`, `DateTimeComponents`, `AndroidScheduleMode`, `Importance`, `GroupAlertBehavior`, `InterruptionLevel`.
125
+
126
+ **Calls:** `show` ยท `schedule` ยท `periodically_show` ยท `cancel`/`cancel_all`/`cancel_pending` ยท `request_permissions` ยท `are_notifications_enabled` ยท `can_schedule_exact_notifications`/`request_exact_alarm_permission` ยท `request_full_screen_intent_permission` ยท `has`/`request_notification_policy_access` ยท `open_app_notification_settings` ยท `get_pending_notifications`/`get_active_notifications`/`get_notification_app_launch_details` ยท `create`/`delete`/`get_notification_channels` (+ groups) ยท `start`/`stop_foreground_service`.
127
+
128
+ **Errors:** `NotificationError` with a stable `code` โ€” `unsupported_platform`, `init_failed`, `show_failed`, `schedule_failed`, `invalid_action`, `invalid_schedule`, `invalid_style`, `invalid_channel`, `invalid_option`, `invalid_service_id`, `channel_failed`, `foreground_service_failed`, `query_failed`, `permission_failed`, `unknown_method`.
129
+
130
+ ## Limitations
131
+
132
+ - Linux/Web have no OS scheduler; Windows schedules one-shot only.
133
+ - Android channel sound/vibration are immutable after creation; aggressive OEM skins may drop alarms after force-stop; Samsung caps ~500 alarms.
134
+ - Interruptions beyond `passive` on Apple need Xcode capabilities.
135
+ - Not wrapped (upstream niche): HTML-format style flags, `MediaStyle`, action `semanticAction`/`invisible`/`contextual`, CarPlay, message data MIME/URI, Linux custom hints.
136
+ - iOS IPAs need Apple signing and are built owner-side; CI builds every other platform.
137
+
138
+ ## Example
139
+
140
+ `examples/flet_notifications_example` โ€” every API on one screen with a timestamped event log, built for Windows, macOS, Linux, Android and Web on every release.
141
+
142
+ ## License
143
+
144
+ MIT
@@ -0,0 +1,120 @@
1
+ # flet-notifications
2
+
3
+ [![pypi](https://img.shields.io/pypi/v/flet-notifications.svg)](https://pypi.python.org/pypi/flet-notifications)
4
+ [![python](https://img.shields.io/badge/python-%3E%3D3.10-%2334D058)](https://pypi.org/project/flet-notifications)
5
+ [![license](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/Nwokike/flet-notifications/blob/main/LICENSE)
6
+
7
+ Cross-platform local notifications for [Flet](https://flet.dev) apps.
8
+
9
+ A 1:1 wrapping of the official [flutter_local_notifications](https://pub.dev/packages/flutter_local_notifications) Flutter plugin, in the same architecture as [flet-ads](https://pypi.org/project/flet-ads/). One `uv add flet-notifications` covers Android, iOS, macOS, Windows, Linux and Web โ€” no Python dependencies beyond `flet`.
10
+
11
+ ## Test it without an account
12
+
13
+ Every release builds the example app for every platform. Sideload any of them and every button is live:
14
+
15
+ | Variant | Download | Notes |
16
+ | :--- | :---: | :--- |
17
+ | ๐Ÿ“ฑ **ARM64** (most phones/TVs) | [**app-arm64-v8a-release.apk**](https://github.com/Nwokike/flet-notifications/releases/latest/download/app-arm64-v8a-release.apk) | Modern 64-bit Android devices โ€” grab this one unless you know otherwise |
18
+ | ๐Ÿ“ฑ **ARMv7** (older phones/TV boxes) | [**app-armeabi-v7a-release.apk**](https://github.com/Nwokike/flet-notifications/releases/latest/download/app-armeabi-v7a-release.apk) | Legacy 32-bit Android devices |
19
+ | ๐Ÿ’ป **x86_64** (emulators/ChromeOS) | [**app-x86_64-release.apk**](https://github.com/Nwokike/flet-notifications/releases/latest/download/app-x86_64-release.apk) | Android emulators & Chromebooks |
20
+ | ๐Ÿ–ฅ๏ธ **Windows** | [**flet-notifications-example-windows.zip**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-windows.zip) | Portable zip |
21
+ | ๐ŸŽ **macOS** | [**flet-notifications-example-macos.dmg**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-macos.dmg) | Disk image |
22
+ | ๐Ÿง **Linux** | [**tar.gz**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-linux-x86_64.tar.gz) ยท [**deb**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example_amd64.deb) ยท [**rpm**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example_x86_64.rpm) | Bundle or packages |
23
+ | ๐ŸŒ **Web** | [**flet-notifications-example-web.tar.gz**](https://github.com/Nwokike/flet-notifications/releases/latest/download/flet-notifications-example-web.tar.gz) | Static bundle |
24
+
25
+ Plain `flet run` launches the prebuilt client without the extension's Dart half โ€” notifications need a built app (an artifact above, or `flet build <platform>`).
26
+
27
+ ## Platform Support
28
+
29
+ | Platform | Show | Schedule | Actions & callbacks | Channels | Progress | Notes |
30
+ |----------|:----:|:--------:|:-------------------:|:--------:|:--------:|-------|
31
+ | iOS | โœ… | โœ… | โœ… | โ€” | โ€” | 13+; attachments, interruption levels |
32
+ | Android | โœ… | โœ… | โœ… | โœ… | โœ… | 7.0+ (API 24); Android TV works |
33
+ | macOS | โœ… | โœ… | โœ… | โ€” | โ€” | 10.15+ |
34
+ | Windows | โœ… | one-shot | โœ… | โ€” | โœ… | 10+; `cancel`/queries need MSIX identity |
35
+ | Linux | โœ… | โ€” | โœ… | โ€” | โ€” | D-Bus server; capabilities vary |
36
+ | Web | โœ… | โ€” | Chrome/Edge only | โ€” | โ€” | Notifications API; gesture-gated permissions |
37
+
38
+ ## Usage
39
+
40
+ ```bash
41
+ uv add flet-notifications
42
+ ```
43
+
44
+ ```python
45
+ import flet as ft
46
+ from flet_notifications import LocalNotifications
47
+
48
+ def main(page: ft.Page):
49
+ notifications = LocalNotifications()
50
+
51
+ async def on_tap(e):
52
+ print("tapped:", e.payload)
53
+
54
+ notifications.on_tap = on_tap
55
+
56
+ if await notifications.request_permissions():
57
+ await notifications.show(id=1, title="Hello", body="Hello!")
58
+
59
+ ft.run(main)
60
+ ```
61
+
62
+ ## Controls
63
+
64
+ | Control | Base | Purpose |
65
+ |---|---|---|
66
+ | `LocalNotifications` | `ft.Service` | Show/schedule/cancel, permissions, probes, channels; construct once, it registers itself |
67
+
68
+ ### Event handlers
69
+
70
+ ```python
71
+ notifications.on_tap = on_tap # typed: NotificationEvent
72
+ notifications.on_action = on_action # typed: e.action_id, e.input, e.data
73
+ notifications.on_dismissed = handler # typed
74
+ ```
75
+
76
+ Typed handlers must use `ft.EventHandler[NotificationEvent]`, **not** `ft.ControlEventHandler[NotificationEvent]` โ€” `ControlEventHandler[X]` double-wraps `X` in `Event[...]`, so `e.payload` raises `AttributeError`. `NotificationEvent` carries `id`, `action_id`, `payload`, `input`, `data` and `response_type`.
77
+
78
+ Taps that arrive while the app wasn't running are replayed on the next start (Android background taps, iOS/macOS cold-start launches).
79
+
80
+ ### Configuration
81
+
82
+ Scheduling on Android needs permissions in the app's `pyproject.toml`:
83
+
84
+ ```toml
85
+ [tool.flet.android.permission]
86
+ RECEIVE_BOOT_COMPLETED = true # restore alarms after reboot
87
+ SCHEDULE_EXACT_ALARM = true # exact schedule modes
88
+ ```
89
+
90
+ The scheduled-notification receivers are `<application>` children, which `[tool.flet.android.*]` cannot declare โ€” the `flet-notifications-patch` console script injects them (plus the core-library-desugaring Gradle flags the plugin needs), idempotently:
91
+
92
+ ```bash
93
+ uv run flet build apk --yes --project myapp --org com.example -v
94
+ uv run flet-notifications-patch --project-root build/flutter
95
+ cd build/flutter/android && ./gradlew assembleRelease -Psplit-per-abi=true
96
+ ```
97
+
98
+ ## Configuration reference
99
+
100
+ **Types:** `NotificationAction`/`NotificationActionInput` (buttons, inline reply) ยท `BigTextStyle`/`BigPictureStyle`/`InboxStyle`/`MessagingStyle` (+ `Person`, `Message`, `AndroidIcon`) ยท `NotificationChannel` (importance, sound, vibration, badge, DND, lights) ยท `NotificationOptions` with `AndroidOptions`/`DarwinOptions`/`LinuxOptions`/`WindowsOptions`/`WebOptions` (the full per-platform detail surface) ยท enums `RepeatInterval`, `DateTimeComponents`, `AndroidScheduleMode`, `Importance`, `GroupAlertBehavior`, `InterruptionLevel`.
101
+
102
+ **Calls:** `show` ยท `schedule` ยท `periodically_show` ยท `cancel`/`cancel_all`/`cancel_pending` ยท `request_permissions` ยท `are_notifications_enabled` ยท `can_schedule_exact_notifications`/`request_exact_alarm_permission` ยท `request_full_screen_intent_permission` ยท `has`/`request_notification_policy_access` ยท `open_app_notification_settings` ยท `get_pending_notifications`/`get_active_notifications`/`get_notification_app_launch_details` ยท `create`/`delete`/`get_notification_channels` (+ groups) ยท `start`/`stop_foreground_service`.
103
+
104
+ **Errors:** `NotificationError` with a stable `code` โ€” `unsupported_platform`, `init_failed`, `show_failed`, `schedule_failed`, `invalid_action`, `invalid_schedule`, `invalid_style`, `invalid_channel`, `invalid_option`, `invalid_service_id`, `channel_failed`, `foreground_service_failed`, `query_failed`, `permission_failed`, `unknown_method`.
105
+
106
+ ## Limitations
107
+
108
+ - Linux/Web have no OS scheduler; Windows schedules one-shot only.
109
+ - Android channel sound/vibration are immutable after creation; aggressive OEM skins may drop alarms after force-stop; Samsung caps ~500 alarms.
110
+ - Interruptions beyond `passive` on Apple need Xcode capabilities.
111
+ - Not wrapped (upstream niche): HTML-format style flags, `MediaStyle`, action `semanticAction`/`invisible`/`contextual`, CarPlay, message data MIME/URI, Linux custom hints.
112
+ - iOS IPAs need Apple signing and are built owner-side; CI builds every other platform.
113
+
114
+ ## Example
115
+
116
+ `examples/flet_notifications_example` โ€” every API on one screen with a timestamped event log, built for Windows, macOS, Linux, Android and Web on every release.
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,56 @@
1
+ [project]
2
+ name = "flet-notifications"
3
+ version = "0.1.0"
4
+ description = "Cross-platform local notifications for Flet apps (Android, iOS, macOS, Windows, Linux, Web)."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Onyeka Nwokike", email = "nwokikeonyeka@gmail.com" }
8
+ ]
9
+ license = { text = "MIT" }
10
+ requires-python = ">=3.10"
11
+ keywords = ["flet", "notifications", "desktop", "android", "ios", "cross-platform"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Software Development :: Libraries :: Python Modules",
20
+ "Intended Audience :: Developers",
21
+ ]
22
+ dependencies = [
23
+ "flet>=1.0.0",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/Nwokike/flet-notifications"
28
+ Repository = "https://github.com/Nwokike/flet-notifications"
29
+ Issues = "https://github.com/Nwokike/flet-notifications/issues"
30
+
31
+ [project.scripts]
32
+ flet-notifications-patch = "flet_notifications.patcher:main"
33
+
34
+ [tool.setuptools.package-data]
35
+ "flutter.flet_notifications" = ["pubspec.yaml", "lib/**"]
36
+
37
+ [tool.setuptools]
38
+ license-files = ["LICENSE"]
39
+
40
+ [build-system]
41
+ requires = ["setuptools"]
42
+ build-backend = "setuptools.build_meta"
43
+
44
+ [dependency-groups]
45
+ dev = [
46
+ "pytest>=8.0",
47
+ "pytest-asyncio>=0.24",
48
+ ]
49
+
50
+ [tool.pytest.ini_options]
51
+ asyncio_mode = "auto"
52
+
53
+ [tool.ruff.lint]
54
+ # Match the flet core source style: Optional[...] and quoted forward
55
+ # references in control/event annotations.
56
+ ignore = ["UP045", "UP037"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,91 @@
1
+ """flet-notifications: cross-platform local notifications for Flet apps.
2
+
3
+ Wraps the battle-tested ``flutter_local_notifications`` Flutter plugin and
4
+ exposes it as a native Flet service (``ft.Service``), following the same
5
+ extension architecture as ``flet-ads`` / ``flet-yandex-ads``.
6
+ """
7
+
8
+ from flet_notifications.actions import NotificationAction, NotificationActionInput
9
+ from flet_notifications.base import (
10
+ NotificationError,
11
+ NotificationErrorCode,
12
+ NotificationEvent,
13
+ ResponseType,
14
+ )
15
+ from flet_notifications.channels import NotificationChannel
16
+ from flet_notifications.events import (
17
+ ActiveNotification,
18
+ LaunchDetails,
19
+ PendingNotification,
20
+ )
21
+ from flet_notifications.local_notifications import LocalNotifications
22
+ from flet_notifications.options import (
23
+ AndroidOptions,
24
+ DarwinOptions,
25
+ InterruptionLevel,
26
+ LinuxOptions,
27
+ LinuxUrgency,
28
+ NotificationOptions,
29
+ Priority,
30
+ Visibility,
31
+ WebOptions,
32
+ WindowsOptions,
33
+ WindowsScenario,
34
+ )
35
+ from flet_notifications.styles import (
36
+ AndroidIcon,
37
+ BigPictureStyle,
38
+ BigTextStyle,
39
+ InboxStyle,
40
+ Message,
41
+ MessagingStyle,
42
+ Person,
43
+ Style,
44
+ )
45
+ from flet_notifications.types import (
46
+ AndroidScheduleMode,
47
+ DateTimeComponents,
48
+ GroupAlertBehavior,
49
+ Importance,
50
+ RepeatInterval,
51
+ )
52
+
53
+ __version__ = "0.1.0"
54
+
55
+ __all__ = [
56
+ "ActiveNotification",
57
+ "AndroidIcon",
58
+ "AndroidOptions",
59
+ "AndroidScheduleMode",
60
+ "BigPictureStyle",
61
+ "BigTextStyle",
62
+ "DarwinOptions",
63
+ "DateTimeComponents",
64
+ "GroupAlertBehavior",
65
+ "Importance",
66
+ "InboxStyle",
67
+ "InterruptionLevel",
68
+ "LaunchDetails",
69
+ "LinuxOptions",
70
+ "LinuxUrgency",
71
+ "LocalNotifications",
72
+ "Message",
73
+ "MessagingStyle",
74
+ "NotificationAction",
75
+ "NotificationActionInput",
76
+ "NotificationChannel",
77
+ "NotificationError",
78
+ "NotificationErrorCode",
79
+ "NotificationEvent",
80
+ "NotificationOptions",
81
+ "PendingNotification",
82
+ "Person",
83
+ "Priority",
84
+ "RepeatInterval",
85
+ "ResponseType",
86
+ "Style",
87
+ "Visibility",
88
+ "WebOptions",
89
+ "WindowsOptions",
90
+ "WindowsScenario",
91
+ ]
@@ -0,0 +1,55 @@
1
+ """Action buttons and inline-reply inputs for notifications."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Optional
7
+
8
+
9
+ @dataclass(kw_only=True)
10
+ class NotificationActionInput:
11
+ """Inline-reply input attached to a notification action.
12
+
13
+ Maps to ``AndroidNotificationActionInput`` on Android, the text variant of
14
+ ``DarwinNotificationAction`` on iOS/macOS and ``WindowsInput`` on Windows.
15
+ """
16
+
17
+ label: str = "" # placeholder / button title over the text field
18
+ choices: list[str] = field(default_factory=list) # quick-reply suggestions
19
+ allow_free_form_input: bool = True
20
+ allowed_mime_types: list[str] = field(default_factory=list)
21
+
22
+ def to_dict(self) -> dict:
23
+ return {
24
+ "label": self.label,
25
+ "choices": list(self.choices),
26
+ "allow_free_form_input": self.allow_free_form_input,
27
+ "allowed_mime_types": list(self.allowed_mime_types),
28
+ }
29
+
30
+
31
+ @dataclass(kw_only=True)
32
+ class NotificationAction:
33
+ """A button shown on a notification.
34
+
35
+ When the button is pressed, ``on_action`` fires with ``action_id == id``.
36
+ If ``input`` is set, the OS shows an inline-reply field and the typed text
37
+ arrives as ``NotificationEvent.input``.
38
+ """
39
+
40
+ id: str
41
+ title: str
42
+ destructive: bool = False # iOS/macOS: render as destructive
43
+ foreground: bool = True # bring the app to the foreground on press
44
+ cancel_notification: bool = True # Android: dismiss the notification
45
+ input: Optional[NotificationActionInput] = None
46
+
47
+ def to_dict(self) -> dict:
48
+ return {
49
+ "id": self.id,
50
+ "title": self.title,
51
+ "destructive": self.destructive,
52
+ "foreground": self.foreground,
53
+ "cancel_notification": self.cancel_notification,
54
+ "input": self.input.to_dict() if self.input else None,
55
+ }
@@ -0,0 +1,87 @@
1
+ """Shared types and helpers for flet-notifications."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+
8
+ import flet as ft
9
+
10
+
11
+ class ResponseType(str, Enum):
12
+ """Type of a notification response delivered from the OS to the app."""
13
+
14
+ TAP = "tap"
15
+ ACTION = "action"
16
+ DISMISSED = "dismissed"
17
+
18
+
19
+ @dataclass
20
+ class NotificationEvent(ft.Event[ft.EventControlType]):
21
+ """A notification tap, action-button press or dismissal.
22
+
23
+ Delivered to ``on_tap`` / ``on_action`` / ``on_dismissed`` handlers.
24
+ """
25
+
26
+ id: int = 0
27
+ action_id: str = "" # "" for a plain tap; the button id for actions
28
+ payload: str = ""
29
+ input: str = "" # inline-reply text, when the action had an input
30
+ response_type: str = ResponseType.TAP.value
31
+ # Windows action inputs arrive in the inherited `data` field, keyed by
32
+ # the input id (Windows leaves `input` null).
33
+
34
+
35
+ class NotificationErrorCode:
36
+ """Stable machine-readable error codes raised by the service."""
37
+
38
+ UNSUPPORTED_PLATFORM = "unsupported_platform"
39
+ INIT_FAILED = "init_failed"
40
+ SHOW_FAILED = "show_failed"
41
+ SCHEDULE_FAILED = "schedule_failed"
42
+ INVALID_ACTION = "invalid_action"
43
+ INVALID_SCHEDULE = "invalid_schedule"
44
+ INVALID_STYLE = "invalid_style"
45
+ INVALID_CHANNEL = "invalid_channel"
46
+ INVALID_SERVICE_ID = "invalid_service_id"
47
+ INVALID_OPTION = "invalid_option"
48
+ CHANNEL_FAILED = "channel_failed"
49
+ FOREGROUND_SERVICE_FAILED = "foreground_service_failed"
50
+ QUERY_FAILED = "query_failed"
51
+ PERMISSION_FAILED = "permission_failed"
52
+ UNKNOWN_METHOD = "unknown_method"
53
+
54
+
55
+ class NotificationError(Exception):
56
+ """Raised when a notification operation fails.
57
+
58
+ ``code`` is a stable machine-readable identifier โ€” see
59
+ :class:`NotificationErrorCode` for the full list.
60
+ """
61
+
62
+ def __init__(self, code: str, message: str = "") -> None:
63
+ self.code = code
64
+ self.message = message
65
+ super().__init__(f"{code}: {message}" if message else code)
66
+
67
+ def __repr__(self) -> str:
68
+ return f"NotificationError(code={self.code!r}, message={self.message!r})"
69
+
70
+
71
+ def check_result(result: object) -> None:
72
+ """Raise :class:`NotificationError` if a Dart invoke returned an error string.
73
+
74
+ Dart service methods return ``"error:<code>:<message>"`` on failure.
75
+ """
76
+ if isinstance(result, str) and result.startswith("error:"):
77
+ _, _, rest = result.partition(":")
78
+ code, _, message = rest.partition(":")
79
+ raise NotificationError(code.strip(), message.strip())
80
+
81
+
82
+ def to_bool(result: object) -> bool:
83
+ """Coerce a Dart invoke result to bool (``"true"``/``"false"``)."""
84
+ check_result(result)
85
+ if isinstance(result, bool):
86
+ return result
87
+ return str(result).strip().lower() == "true"
@@ -0,0 +1,87 @@
1
+ """Android notification channels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Optional
7
+
8
+ from flet_notifications.types import Importance
9
+
10
+
11
+ @dataclass(kw_only=True)
12
+ class NotificationChannel:
13
+ """An Android notification channel (required since Android 8.0).
14
+
15
+ Sound and vibration are fixed at channel creation; to change them you
16
+ create a new channel with a new id.
17
+ """
18
+
19
+ id: str
20
+ name: str
21
+ description: str = ""
22
+ group_id: Optional[str] = None
23
+ importance: Importance = Importance.DEFAULT
24
+ bypass_dnd: bool = False
25
+ play_sound: bool = True
26
+ sound: Optional[str] = None # raw resource name, e.g. "alert"
27
+ enable_vibration: bool = True
28
+ vibration_pattern: Optional[list[int]] = None
29
+ show_badge: bool = True
30
+ enable_lights: bool = False
31
+ led_color: Optional[str] = None # #RRGGBB
32
+
33
+ def __post_init__(self) -> None:
34
+ if not self.id:
35
+ raise ValueError("channel id is required")
36
+ if not self.name:
37
+ raise ValueError("channel name is required")
38
+ if self.led_color is not None:
39
+ self.led_color = _validate_hex(self.led_color)
40
+
41
+ def to_dict(self) -> dict:
42
+ return {
43
+ "id": self.id,
44
+ "name": self.name,
45
+ "description": self.description,
46
+ "group_id": self.group_id,
47
+ "importance": self.importance.value,
48
+ "bypass_dnd": self.bypass_dnd,
49
+ "play_sound": self.play_sound,
50
+ "sound": self.sound,
51
+ "enable_vibration": self.enable_vibration,
52
+ "vibration_pattern": (
53
+ list(self.vibration_pattern) if self.vibration_pattern else None
54
+ ),
55
+ "show_badge": self.show_badge,
56
+ "enable_lights": self.enable_lights,
57
+ "led_color": self.led_color,
58
+ }
59
+
60
+ @classmethod
61
+ def from_dict(cls, data: dict) -> "NotificationChannel":
62
+ return cls(
63
+ id=str(data.get("id", "")),
64
+ name=str(data.get("name", "")),
65
+ description=str(data.get("description") or ""),
66
+ group_id=data.get("groupId") or data.get("group_id"),
67
+ importance=Importance(data.get("importance", "defaultImportance")),
68
+ bypass_dnd=bool(data.get("bypass_dnd", False)),
69
+ play_sound=bool(data.get("play_sound", True)),
70
+ sound=data.get("sound"),
71
+ enable_vibration=bool(data.get("enable_vibration", True)),
72
+ vibration_pattern=data.get("vibration_pattern"),
73
+ show_badge=bool(data.get("show_badge", True)),
74
+ enable_lights=bool(data.get("enable_lights", False)),
75
+ led_color=data.get("led_color"),
76
+ )
77
+
78
+
79
+ def _validate_hex(value: str) -> str:
80
+ v = value.strip()
81
+ if not v.startswith("#") or len(v) not in (7, 9):
82
+ raise ValueError(f"invalid hex color: {value!r}")
83
+ try:
84
+ int(v[1:], 16)
85
+ except ValueError as e:
86
+ raise ValueError(f"invalid hex color: {value!r}") from e
87
+ return v