Native local notifications for Flet apps (Android)
Project description
flet-android-notifications
Native Android notifications for Flet apps. Bridges Python to the flutter_local_notifications plugin through a custom Flet extension.
Flet has no built-in notification support, and Python-side approaches (plyer, Pyjnius) fail because Flet's Python process is sandboxed from Android APIs.
Install
pip install flet-android-notifications
Add to your pyproject.toml:
[project]
dependencies = ["flet>=0.80.5", "flet-android-notifications"]
[tool.flet.android.permission]
"android.permission.POST_NOTIFICATIONS" = true
"android.permission.SCHEDULE_EXACT_ALARM" = true # for scheduled/periodic
"android.permission.RECEIVE_BOOT_COMPLETED" = true # survive reboots
[tool.flet.app]
exclude = ["flet_android_notifications"]
[tool.flet.dev_packages]
flet-android-notifications = "flet_android_notifications"
Quick start
from datetime import datetime, timedelta
import flet as ft
from flet_android_notifications import FletAndroidNotifications
def main(page: ft.Page):
notifications = FletAndroidNotifications()
async def send(e):
await notifications.request_permissions()
await notifications.show_notification(
notification_id=1, title="Hello", body="It works!",
)
page.add(ft.Button(content="Send", on_click=send))
ft.run(main)
Instantiate FletAndroidNotifications once. Don't add it to page.overlay or page.controls — it's a service, not a visual control.
See examples/ for more: simple, action buttons, scheduled, styles, periodic, timeout, query, foreground service.
API overview
Core methods
| Method | Description |
|---|---|
show_notification(id, title, body, ...) |
show a notification immediately |
schedule_notification(id, title, body, scheduled_time, ...) |
fire at a future time via AlarmManager |
periodically_show(id, title, body, repeat_interval, ...) |
repeat every minute / hour / day / week |
periodically_show_with_duration(id, title, body, duration_seconds, ...) |
repeat at a custom interval |
start_foreground_service(id, title, body, ...) |
start a foreground service with persistent notification |
stop_foreground_service() |
stop the foreground service and remove its notification |
cancel(notification_id) |
cancel one notification |
cancel_all() |
cancel all notifications |
Query methods
| Method | Returns |
|---|---|
get_active_notifications() |
list[dict] — currently displayed (id, title, body, channel_id, payload) |
get_pending_notifications() |
list[dict] — scheduled/periodic (id, title, body, payload) |
Permission methods
| Method | Returns |
|---|---|
request_permissions() |
bool — POST_NOTIFICATIONS (Android 13+) |
request_exact_alarm_permission() |
bool — SCHEDULE_EXACT_ALARM (Android 14+) |
Tap callback
import json
def on_tap(e):
data = json.loads(e.data) # {"payload": "...", "action_id": "..."}
notifications = FletAndroidNotifications(on_notification_tap=on_tap)
action_id is "" when the body is tapped (not an action button).
Notification parameters
show_notification, schedule_notification, periodically_show, and periodically_show_with_duration all share a common set of parameters. Only the required ones differ per method.
Required parameters
| Parameter | show |
schedule |
periodically_show |
periodically_show_with_duration |
|---|---|---|---|---|
notification_id |
int | int | int | int |
title |
str | str | str | str |
body |
str | str | str | str |
scheduled_time |
— | datetime | — | — |
repeat_interval |
— | — | str | — |
duration_seconds |
— | — | — | int|float |
repeat_interval is one of "every_minute", "hourly", "daily", "weekly".
Common optional parameters
These work on all four methods above.
Basics:
| Parameter | Type | Default | Description |
|---|---|---|---|
payload |
str |
"" |
returned in tap callback |
actions |
list[dict] |
None |
buttons: [{"id": "...", "title": "..."}] |
importance |
str |
"high" |
none, min, low, default, high, max |
timeout_after |
int|None |
None |
auto-dismiss after N milliseconds |
Channel:
| Parameter | Type | Default |
|---|---|---|
channel_id |
str |
"flet_notifications" |
channel_name |
str |
"Flet Notifications" |
channel_description |
str |
"Notifications from Flet app" |
channel_bypass_dnd |
bool |
False |
Appearance:
| Parameter | Type | Default | Description |
|---|---|---|---|
icon |
str|None |
None |
drawable resource for small icon |
large_icon |
str|None |
None |
thumbnail on right side |
large_icon_type |
str |
"drawable_resource" |
or "file_path" |
color |
str|None |
None |
hex accent color, e.g. "#FF5722" |
colorized |
bool |
False |
color as background (foreground service only) |
sub_text |
str|None |
None |
small text below content |
visibility |
str|None |
None |
"public", "private", or "secret" |
Behavior:
| Parameter | Type | Default | Description |
|---|---|---|---|
play_sound |
bool |
True |
play notification sound |
enable_vibration |
bool |
True |
vibrate |
sound |
str|None |
None |
raw resource name (e.g. "alert_tone") |
vibration_pattern |
list[int]|None |
None |
e.g. [0, 500, 200, 500] |
ongoing |
bool |
False |
can't be swiped away |
auto_cancel |
bool |
True |
dismiss on tap |
silent |
bool |
False |
suppress sound and vibration |
only_alert_once |
bool |
False |
alert on first show only |
Styles and progress:
| Parameter | Type | Default | Description |
|---|---|---|---|
style |
BigTextStyle|BigPictureStyle|InboxStyle|None |
None |
rich expandable style |
show_progress |
bool |
False |
show progress bar |
max_progress |
int |
0 |
max value |
progress |
int |
0 |
current value |
indeterminate |
bool |
False |
spinning progress bar |
Grouping:
| Parameter | Type | Default | Description |
|---|---|---|---|
group_key |
str|None |
None |
bundle notifications together |
set_as_group_summary |
bool |
False |
this is the group summary |
group_alert_behavior |
str |
"all" |
"all", "summary", "children" |
Schedule-only parameters
These only apply to schedule_notification:
| Parameter | Type | Default | Description |
|---|---|---|---|
schedule_mode |
str |
"inexact_allow_while_idle" |
see table below |
match_date_time_components |
str|None |
None |
"time" (daily), "day_of_week_and_time" (weekly), "day_of_month_and_time" (monthly), "date_and_time" (yearly) |
Schedule modes:
| Mode | Exact alarm permission? | Fires in Doze? |
|---|---|---|
"inexact" |
no | no |
"inexact_allow_while_idle" |
no | yes |
"exact" |
yes | no |
"exact_allow_while_idle" |
yes | yes |
"alarm_clock" |
yes | yes |
Foreground service
For persistent background tasks (music, GPS tracking, uploads) that require a visible notification:
await notifications.start_foreground_service(
notification_id=1, # must not be 0
title="Uploading",
body="3 files remaining...",
foreground_service_types=["special_use"],
ongoing=True,
)
# when done:
await notifications.stop_foreground_service()
Parameters specific to foreground service:
| Parameter | Type | Default | Description |
|---|---|---|---|
start_type |
str |
"start_sticky" |
start_sticky, start_not_sticky, start_sticky_compatibility, start_redeliver_intent |
foreground_service_types |
list[str]|None |
None |
e.g. ["special_use"], ["location"], ["media_playback"] |
All other notification parameters (channel, appearance, behavior, etc.) are the same as show_notification.
Important:
notification_idmust not be 0 (Android constraint)- The notification is not removed by
cancel()orcancel_all()— usestop_foreground_service() - Requires
FOREGROUND_SERVICEpermission plus a type-specific permission (e.g.FOREGROUND_SERVICE_SPECIAL_USE)
AndroidManifest.xml — add inside <application>:
<service android:name="com.dexterous.flutterlocalnotifications.ForegroundService"
android:exported="false"
android:foregroundServiceType="specialUse" />
Adjust foregroundServiceType to match your use case (e.g. location, mediaPlayback).
pyproject.toml permissions:
[tool.flet.android.permission]
"android.permission.FOREGROUND_SERVICE" = true
"android.permission.FOREGROUND_SERVICE_SPECIAL_USE" = true
Styles
from flet_android_notifications import BigTextStyle, BigPictureStyle, InboxStyle
# expandable long text
style=BigTextStyle("Full text here...", content_title="Expanded title")
# full-width image when expanded
style=BigPictureStyle(drawable_resource="splash")
# list of lines
style=InboxStyle(["Line 1", "Line 2", "Line 3"], summary_text="3 items")
Building the APK
# first build — generates Flutter template, may fail at Gradle
flet build apk -v
# patch desugaring into build/flutter/android/app/build.gradle.kts:
# android { compileOptions { isCoreLibraryDesugaringEnabled = true } }
# dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") }
# rebuild
flet build apk -v
The desugaring patch is needed because flutter_local_notifications v19+ uses Java 8 APIs. Apply once per clean build directory.
AndroidManifest.xml for scheduled notifications
Register BroadcastReceivers inside <application> in build/flutter/android/app/src/main/AndroidManifest.xml so scheduled notifications survive reboots:
<receiver android:exported="false"
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false"
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
Installing on device
Always full-uninstall before installing. Flet caches the extracted Python environment:
adb uninstall com.yourapp.package
adb install build/apk/app-release.apk
On Windows, set PYTHONIOENCODING=utf-8 before building to avoid Unicode crashes.
Custom resources
- Small icons: vector drawable XML in
res/drawable/(24dp, white on transparent) - Sounds: audio files in
res/raw/, reference by name without extension:sound="alert_tone"
Add res/raw/keep.xml to prevent resource stripping:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@raw/*,@drawable/ic_*" />
Sound is permanently bound to a channel at creation. Change the sound by using a different channel_id.
Samsung OneUI notes
- Color: Samsung's system palette overrides the
colorparameter. Works on stock Android, ignored on Samsung. - Brief mode: Samsung's compact notification view hides expanded content. Swipe down to expand.
colorized: only works for foreground service / media-style notifications (all OEMs).
Limitations
- Android only. iOS support would need
DarwinNotificationDetailsin the Dart layer. - Desktop: the service instantiates without error but notifications won't appear.
How it works
Python app → FletAndroidNotifications (ft.Service)
→ _invoke_method() over Flet protocol
→ NotificationsService (FletService, Dart)
→ flutter_local_notifications plugin → Android NotificationManager
The extension ships as a Python package with a flutter/ directory containing the Dart code. flet build apk discovers it in site-packages and includes it as a Flutter path dependency.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flet_android_notifications-0.7.2.tar.gz.
File metadata
- Download URL: flet_android_notifications-0.7.2.tar.gz
- Upload date:
- Size: 16.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c44620633e79306db3c3505c901c5cfa3672d59097cb24559d86a9899ea07ee
|
|
| MD5 |
d51de6e7c3a0ba2f7af488f47d657c58
|
|
| BLAKE2b-256 |
f98f49909ef5dfd480387533bdddf6ca1feaacc5ecaa127795a7aa625a68a8e7
|
File details
Details for the file flet_android_notifications-0.7.2-py3-none-any.whl.
File metadata
- Download URL: flet_android_notifications-0.7.2-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb092ef0e2c3b193fd807cdf2d0114dba055b962f16c8db954d19ed7befc3f69
|
|
| MD5 |
61208f7adf9152810c056fcc1bf493ab
|
|
| BLAKE2b-256 |
1cd0b9d8f9468fe01facf835a165da187a48cad1afab2252149d2f9cf84a5911
|