{"id":32281085,"url":"https://github.com/brunobrown/flet-onesignal","last_synced_at":"2026-02-19T08:07:02.101Z","repository":{"id":269272619,"uuid":"906855596","full_name":"brunobrown/flet-onesignal","owner":"brunobrown","description":"Flutter OneSignal package integration for Python Flet.","archived":false,"fork":false,"pushed_at":"2026-02-14T18:48:24.000Z","size":169,"stargazers_count":16,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-15T02:47:48.189Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brunobrown.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-22T05:28:37.000Z","updated_at":"2026-02-14T18:30:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"92df2a06-2890-4087-bf95-c9c85d93666d","html_url":"https://github.com/brunobrown/flet-onesignal","commit_stats":null,"previous_names":["brunobrown/onesignal-flet","brunobrown/flet-onesignal"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/brunobrown/flet-onesignal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-onesignal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-onesignal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-onesignal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-onesignal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brunobrown","download_url":"https://codeload.github.com/brunobrown/flet-onesignal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-onesignal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29608152,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T06:47:36.664Z","status":"ssl_error","status_checked_at":"2026-02-19T06:45:47.551Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-23T00:54:09.452Z","updated_at":"2026-02-19T08:07:02.095Z","avatar_url":"https://github.com/brunobrown.png","language":"Python","funding_links":["https://www.buymeacoffee.com/brunobrown"],"categories":["Extensions"],"sub_categories":["Community Extensions"],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/user-attachments/assets/ee3f4caf-10a7-4c58-948d-6a59fda97850\" width=\"300\" height=\"150\" alt=\"Flet OneSignal\"\u003e\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eFlet OneSignal\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eOneSignal SDK integration for Flet applications\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/brunobrown/flet-asp/blob/main/LICENSE\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-MIT-green?style=flat\" alt=\"License\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/brunobrown/flet-onesignal/actions?query=workflow%3AMain+event%3Apush+branch%3Amain\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://github.com/brunobrown/flet-onesignal/actions/workflows/main.yml/badge.svg?event=push\u0026branch=main\" alt=\"Main\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/brunobrown/flet-onesignal/actions?query=workflow%3ADev+event%3Apush+branch%3ADev\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://github.com/brunobrown/flet-onesignal/actions/workflows/dev.yml/badge.svg?event=push\u0026branch=dev\" alt=\"Dev\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/flet-onesignal\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/v/flet-onesignal?color=%2334D058\u0026label=pypi%20package\" alt=\"Package version\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/flet-onesignal\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/pyversions/flet-onesignal.svg?color=%2334D058\" alt=\"Supported Python versions\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pepy.tech/projects/flet-onesignal\"\u003e\u003cimg src=\"https://static.pepy.tech/personalized-badge/flet-onesignal?period=monthly\u0026units=INTERNATIONAL_SYSTEM\u0026left_color=GREY\u0026right_color=BLUE\u0026left_text=downloads%2Fmonth\" alt=\"PyPI Downloads\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [API Reference](#api-reference)\n\n## Overview\n\n**Flet OneSignal** is an extension that integrates the [OneSignal Flutter SDK](https://documentation.onesignal.com/docs/flutter-sdk-setup) with [Flet](https://flet.dev) applications. It provides a complete Python API for:\n\n- [Push Notifications](#push-notifications) — send and receive on iOS and Android ([OneSignal Docs](https://documentation.onesignal.com/docs/en/push-notification-guide))\n- [In-App Messages](#in-app-messages) — targeted messages within your app ([OneSignal Docs](https://documentation.onesignal.com/docs/en/in-app-messages-quickstart))\n- [User Management](#user-management) — identity, tags, aliases, email, SMS ([OneSignal Docs](https://documentation.onesignal.com/docs/en/users))\n- [Location](#location) — geo-targeted messaging ([OneSignal Docs](https://documentation.onesignal.com/docs/en/location-data))\n- [Outcomes](#outcomes) — track actions and conversions ([OneSignal Docs](https://documentation.onesignal.com/docs/en/outcomes))\n- [Live Activities](#live-activities-ios) — iOS real-time updates (iOS 16.1+) ([OneSignal Docs](https://documentation.onesignal.com/docs/en/live-activities))\n- [Privacy \u0026 Consent](#privacy--consent) — GDPR compliance ([OneSignal Docs](https://documentation.onesignal.com/docs/en/handling-personal-data))\n- [Debugging](#debugging) — log levels and error handling\n\n\u003e **Version 0.4.0** - Built for Flet 0.80.x with a modular architecture that mirrors the OneSignal SDK structure.\n\n---\n\n## Buy Me a Coffee\n\nIf you find this project useful, please consider supporting its development:\n\n\u003ca href=\"https://www.buymeacoffee.com/brunobrown\"\u003e\n\u003cimg src=\"https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-1.svg\" width=\"200\" alt=\"Buy Me a Coffee\"\u003e\n\u003c/a\u003e\n\n---\n\n## Requirements\n\n| Component | Minimum Version |\n|-----------|-----------------|\n| Python | 3.10+           |\n| Flet | 0.80.x+         |\n\n\n### Platform Requirements\n\n| Platform | Minimum Version | Notes |\n|----------|-----------------|-------|\n| **iOS** | 12.0+ | Requires Xcode 14+ |\n| **Android** | API 24 (Android 7.0)+ | Requires `compileSdkVersion 33+` |\n\n---\n\n## Installation\n\n### Step 1: Install the Package\n\nChoose your preferred package manager:\n\n```bash\n# Using UV (Recommended)\nuv add flet-onesignal\n\n# Using pip\npip install flet-onesignal\n\n# Using Poetry\npoetry add flet-onesignal\n```\n\n### Step 2: Configure pyproject.toml\n\nAdd the dependency to your project configuration:\n\n```toml\n[project]\nname = \"my-flet-app\"\nversion = \"1.0.0\"\nrequires-python = \"\u003e=3.10\"\n\ndependencies = [\n    \"flet\u003e=0.80.5\",\n    \"flet-onesignal\u003e=0.4.0\",\n]\n\n[tool.flet.app]\npath = \"src\"\n\n```\n\n### Step 3: OneSignal Dashboard Setup (Android)\n\n1. Create an account at [OneSignal.com](https://onesignal.com), then click **+ Create** \u003e **New App**.\n\n2. Enter your **App Name**, select the organization, choose **Google Android (FCM)** as the channel, and click **Next: Configure Your Platform**.\n\n   ![New OneSignal App](https://github.com/user-attachments/assets/b9211026-ae68-4b51-a185-06092300f77f)\n\n\n3. Upload your **Service Account JSON** file. To generate it, go to the [Firebase Console](https://console.firebase.google.com) \u003e **Project Settings** \u003e **Service accounts** \u003e **Generate new private key**. See the [OneSignal Android credentials guide](https://documentation.onesignal.com/docs/en/android-firebase-credentials) for detailed instructions. Click **Save \u0026 Continue**.\n\n\n   ![FCM Configuration](https://github.com/user-attachments/assets/eddd4655-2b99-4c57-b217-e50151014dca)\n\n4. Select **Flutter** as the target SDK, then click **Save \u0026 Continue**.\n\n   ![Select SDK](https://github.com/user-attachments/assets/201ff0ab-83e0-401c-9ad8-0407ba280c49)\n\n\n5. Copy the **App ID** displayed on the screen and click **Done**. You will use this ID in your Flet app.\n\n   ![App ID](https://github.com/user-attachments/assets/90edfacc-d9e7-402e-b76b-9781808167f6)\n\n\n### Step 4: iOS Configuration\n\n1. Enable **Push Notifications** capability in Xcode\n2. Enable **Background Modes** \u003e Remote notifications\n3. Add your APNs certificate to the OneSignal dashboard\n\n---\n\n## Quick Start\n\n```python\nimport flet as ft\nimport flet_onesignal as fos\n\n# Your OneSignal App ID from the dashboard\nONESIGNAL_APP_ID = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n\n\nasync def main(page: ft.Page):\n    page.title = \"My App\"\n\n    # Initialize OneSignal\n    onesignal = fos.OneSignal(\n        app_id=ONESIGNAL_APP_ID,\n        log_level=fos.OSLogLevel.DEBUG,  # Enable debug logging\n    )\n\n    # Add to page services (required for Flet 0.80.x services)\n    page.services.append(onesignal)\n\n    # Request notification permission\n    permission_granted = await onesignal.notifications.request_permission()\n    print(f\"Notification permission: {permission_granted}\")\n\n    # Identify the user (optional but recommended)\n    await onesignal.login(\"user_12345\")\n\n    page.add(ft.Text(\"OneSignal is ready!\"))\n\n\nif __name__ == \"__main__\":\n    ft.run(main)\n```\n\n\u003e **Note:** `OneSignal` is a **service**, not a visual control. You must add it using `page.services.append(onesignal)` — **not** `page.overlay.append(onesignal)`. Using `overlay` will not initialize the SDK correctly.\n\n---\n\n## Architecture\n\nThe SDK follows a modular architecture that mirrors the official OneSignal SDK:\n\n```\nfos.OneSignal\n│\n├── .debug              # Logging and debugging\n├── .user               # User identity, tags, aliases, email, SMS\n├── .notifications      # Push notification management\n├── .in_app_messages    # In-app message triggers and lifecycle\n├── .location           # Location sharing (optional)\n├── .session            # Outcomes and analytics\n└── .live_activities    # iOS Live Activities (iOS 16.1+)\n```\n\nEach module provides focused functionality and can be accessed as a property of the main `OneSignal` instance.\n\n---\n\n## User Management\n\n### Login and Logout\n\nAssociate users with their account in your system using an **External User ID**:\n\n```python\n# Login - Associates the device with your user ID\nawait onesignal.login(\"user_12345\")\n\n# Logout - Removes the association, creates anonymous user\nawait onesignal.logout()\n```\n\n\u003e **Best Practice:** Call `login()` when the user signs into your app and `logout()` when they sign out.\n\n### Get User IDs\n\n```python\n# Get the OneSignal-generated user ID\nonesignal_id = await onesignal.user.get_onesignal_id()\nprint(f\"OneSignal ID: {onesignal_id}\")\n\n# Get the External User ID (set via login)\nexternal_id = await onesignal.user.get_external_id()\nprint(f\"External ID: {external_id}\")\n```\n\n### Tags\n\nTags are key-value pairs used for segmentation and personalization:\n\n```python\n# Add a single tag\nawait onesignal.user.add_tag(\"subscription_type\", \"premium\")\n\n# Add multiple tags at once\nawait onesignal.user.add_tags({\n    \"favorite_team\": \"barcelona\",\n    \"notification_frequency\": \"daily\",\n    \"app_version\": \"2.1.0\",\n})\n\n# Remove a tag\nawait onesignal.user.remove_tag(\"old_tag\")\n\n# Remove multiple tags\nawait onesignal.user.remove_tags([\"tag1\", \"tag2\", \"tag3\"])\n\n# Get all tags\ntags = await onesignal.user.get_tags()\nprint(f\"User tags: {tags}\")\n```\n\n### Aliases\n\nAliases allow you to associate multiple identifiers with a single user:\n\n```python\n# Add an alias (e.g., CRM ID, database ID)\nawait onesignal.user.add_alias(\"crm_id\", \"CRM_98765\")\n\n# Add multiple aliases\nawait onesignal.user.add_aliases({\n    \"database_id\": \"DB_12345\",\n    \"analytics_id\": \"GA_67890\",\n})\n\n# Remove an alias\nawait onesignal.user.remove_alias(\"old_alias\")\n```\n\n### Email Subscriptions\n\nAdd email addresses for omnichannel messaging:\n\n```python\n# Add an email subscription\nawait onesignal.user.add_email(\"user@example.com\")\n\n# Remove an email subscription\nawait onesignal.user.remove_email(\"user@example.com\")\n```\n\n### SMS Subscriptions\n\nAdd phone numbers for SMS messaging (use E.164 format):\n\n```python\n# Add SMS subscription (E.164 format: +[country code][number])\nawait onesignal.user.add_sms(\"+5511999999999\")\n\n# Remove SMS subscription\nawait onesignal.user.remove_sms(\"+5511999999999\")\n```\n\n### Language\n\nSet the user's preferred language for localized notifications:\n\n```python\n# Set language using ISO 639-1 code\nawait onesignal.user.set_language(\"pt\")  # Portuguese\nawait onesignal.user.set_language(\"es\")  # Spanish\nawait onesignal.user.set_language(\"en\")  # English\n\n# You can also use the Language enum for auto-complete support\nawait onesignal.user.set_language(fos.Language.PORTUGUESE.value)\nawait onesignal.user.set_language(fos.Language.SPANISH.value)\n```\n\n---\n\n## Push Notifications\n\n### Requesting Permission\n\nYou must request permission before sending push notifications:\n\n```python\n# Request permission with fallback to settings\ngranted = await onesignal.notifications.request_permission(\n    fallback_to_settings=True  # Opens settings if previously denied\n)\n\nif granted:\n    print(\"User granted notification permission!\")\nelse:\n    print(\"User denied notification permission\")\n```\n\n### Check Permission Status\n\n```python\n# Check if permission can be requested (not yet prompted)\ncan_request = await onesignal.notifications.can_request_permission()\n\n# Check current permission status\nhas_permission = await onesignal.notifications.get_permission()\n```\n\n### iOS Provisional Authorization\n\nRequest provisional (quiet) authorization on iOS 12+:\n\n```python\n# Notifications will be delivered quietly to Notification Center\nauthorized = await onesignal.notifications.register_for_provisional_authorization()\n```\n\n### Managing Notifications\n\n```python\n# Clear all notifications from the notification center\nawait onesignal.notifications.clear_all()\n\n# Remove a specific notification (Android only)\nawait onesignal.notifications.remove_notification(notification_id)\n\n# Remove a group of notifications (Android only)\nawait onesignal.notifications.remove_grouped_notifications(\"group_key\")\n```\n\n### Foreground Display Control\n\nControl whether notifications are shown when the app is in the foreground:\n\n```python\n# Inside on_notification_foreground handler:\n# Prevent a notification from being displayed\nawait onesignal.notifications.prevent_default(e.notification_id)\n\n# Later, allow display if needed\nawait onesignal.notifications.display(e.notification_id)\n```\n\n### Push Subscription Control\n\n```python\n# Opt user into push notifications\nawait onesignal.user.opt_in_push()\n\n# Opt user out of push notifications\nawait onesignal.user.opt_out_push()\n\n# Check if user is opted in\nis_opted_in = await onesignal.user.is_push_opted_in()\n\n# Get push subscription details\nsubscription_id = await onesignal.user.get_push_subscription_id()\npush_token = await onesignal.user.get_push_subscription_token()\n```\n\n### Handling Notification Events\n\n```python\ndef on_notification_click(e: fos.OSNotificationClickEvent):\n    \"\"\"Called when user taps on a notification.\"\"\"\n    print(f\"Notification clicked: {e.notification}\")\n    print(f\"Action ID: {e.action_id}\")  # If action buttons were used\n\n\ndef on_notification_foreground(e: fos.OSNotificationWillDisplayEvent):\n    \"\"\"Called when notification received while app is in foreground.\"\"\"\n    print(f\"Notification received: {e.notification}\")\n    print(f\"Notification ID: {e.notification_id}\")\n\n    # Optionally prevent display and handle manually:\n    # await onesignal.notifications.prevent_default(e.notification_id)\n    # Later, allow display if needed:\n    # await onesignal.notifications.display(e.notification_id)\n\n\ndef on_permission_change(e: fos.OSPermissionChangeEvent):\n    \"\"\"Called when notification permission status changes.\"\"\"\n    print(f\"Permission granted: {e.permission}\")\n\n\n# Register handlers when creating OneSignal instance\nonesignal = fos.OneSignal(\n    app_id=ONESIGNAL_APP_ID,\n    on_notification_click=on_notification_click,\n    on_notification_foreground=on_notification_foreground,\n    on_permission_change=on_permission_change,\n)\n```\n\n---\n\n## In-App Messages\n\nIn-App Messages (IAMs) are messages displayed within your app based on triggers.\n\n### Triggers\n\nTriggers determine when IAMs are displayed:\n\n```python\n# Add a trigger\nawait onesignal.in_app_messages.add_trigger(\"level_completed\", \"5\")\n\n# Add multiple triggers\nawait onesignal.in_app_messages.add_triggers({\n    \"screen\": \"checkout\",\n    \"cart_value\": \"50\",\n})\n\n# Remove a trigger\nawait onesignal.in_app_messages.remove_trigger(\"old_trigger\")\n\n# Remove multiple triggers\nawait onesignal.in_app_messages.remove_triggers([\"trigger1\", \"trigger2\"])\n\n# Clear all triggers\nawait onesignal.in_app_messages.clear_triggers()\n```\n\n### Pausing In-App Messages\n\nTemporarily prevent IAMs from displaying:\n\n```python\n# Pause IAM display\nawait onesignal.in_app_messages.pause()\n\n# Resume IAM display\nawait onesignal.in_app_messages.resume()\n\n# Check if paused\nis_paused = await onesignal.in_app_messages.is_paused()\n```\n\n### IAM Event Handlers\n\n```python\ndef on_iam_click(e: fos.OSInAppMessageClickEvent):\n    \"\"\"Called when user interacts with an IAM.\"\"\"\n    print(f\"IAM clicked - Action: {e.result.action_id}\")\n    print(f\"URL: {e.result.url}\")\n    print(f\"Closing message: {e.result.closing_message}\")\n\n\ndef on_iam_will_display(e: fos.OSInAppMessageWillDisplayEvent):\n    \"\"\"Called before an IAM is displayed.\"\"\"\n    print(f\"IAM will display: {e.message}\")\n\n\ndef on_iam_did_display(e: fos.OSInAppMessageDidDisplayEvent):\n    \"\"\"Called after an IAM is displayed.\"\"\"\n    print(\"IAM displayed\")\n\n\ndef on_iam_will_dismiss(e: fos.OSInAppMessageWillDismissEvent):\n    \"\"\"Called before an IAM is dismissed.\"\"\"\n    print(\"IAM will dismiss\")\n\n\ndef on_iam_did_dismiss(e: fos.OSInAppMessageDidDismissEvent):\n    \"\"\"Called after an IAM is dismissed.\"\"\"\n    print(\"IAM dismissed\")\n\n\nonesignal = fos.OneSignal(\n    app_id=ONESIGNAL_APP_ID,\n    on_iam_click=on_iam_click,\n    on_iam_will_display=on_iam_will_display,\n    on_iam_did_display=on_iam_did_display,\n    on_iam_will_dismiss=on_iam_will_dismiss,\n    on_iam_did_dismiss=on_iam_did_dismiss,\n)\n```\n\n---\n\n## Location\n\nShare user location for geo-targeted messaging:\n\n```python\n# Request location permission\ngranted = await onesignal.location.request_permission()\n\n# Enable location sharing\nawait onesignal.location.set_shared(True)\n\n# Disable location sharing\nawait onesignal.location.set_shared(False)\n\n# Check if location is being shared\nis_shared = await onesignal.location.is_shared()\n```\n\n### Android Setup\n\nOn Android, the OneSignal Location module is **not included by default**. Without it, `set_shared(True)` will log `no location dependency found` and location will not work.\n\nTo enable it, you need to build your app using the `fos-build` CLI, which automatically injects the required Gradle dependencies (`com.onesignal:location`, `play-services-location`, and ProGuard rules).\n\n**1. Install the CLI:**\n\n```bash\n# Using UV (Recommended)\nuv add flet-onesignal[cli]\n\n# Using pip\npip install flet-onesignal[cli]\n\n# Using Poetry\npoetry add flet-onesignal[cli]\n```\n\n**2. Add location permissions** to your `pyproject.toml`:\n\n```toml\n# pyproject.toml\n[tool.flet.android]\npermission.\"android.permission.ACCESS_FINE_LOCATION\" = true\npermission.\"android.permission.ACCESS_COARSE_LOCATION\" = true\n```\n\nThese permissions are required in the Android Manifest for the app to access the device's GPS. `ACCESS_FINE_LOCATION` enables precise GPS positioning, while `ACCESS_COARSE_LOCATION` enables approximate location via Wi-Fi/cell towers. Without them, the system will deny location access at runtime even if the user grants permission in the dialog.\n\n**3. Enable the OneSignal Location module** via `pyproject.toml` or CLI flag:\n\n```toml\n# pyproject.toml\n[tool.flet.onesignal.android]\nlocation = true\n```\n\n```bash\n# Or pass the flag directly\nfos-build apk --location\n```\n\n**4. Build with `fos-build`:**\n\n```bash\nfos-build apk\n```\n\n\u003e **Note:** Using `flet build apk` directly (without `fos-build`) will **not** inject the location module and the feature will silently fail at runtime.\n\n---\n\n## Outcomes\n\nTrack user actions and conversions attributed to notifications:\n\n```python\n# Track a simple outcome\nawait onesignal.session.add_outcome(\"product_viewed\")\n\n# Track a unique outcome (counted once per notification)\nawait onesignal.session.add_unique_outcome(\"app_opened\")\n\n# Track an outcome with a value (e.g., purchase amount)\nawait onesignal.session.add_outcome_with_value(\"purchase\", 29.99)\n```\n\n---\n\n## Live Activities (iOS)\n\nUpdate iOS Live Activities in real-time (iOS 16.1+):\n\n```python\n# Enter a Live Activity\nawait onesignal.live_activities.enter(\n    activity_id=\"delivery_12345\",\n    token=\"live_activity_push_token\"\n)\n\n# Exit a Live Activity\nawait onesignal.live_activities.exit(\"delivery_12345\")\n\n# Set push-to-start token for a Live Activity type\nawait onesignal.live_activities.set_push_to_start_token(\n    activity_type=\"DeliveryActivityAttributes\",\n    token=\"push_to_start_token\"\n)\n\n# Remove push-to-start token\nawait onesignal.live_activities.remove_push_to_start_token(\"DeliveryActivityAttributes\")\n\n# Setup default Live Activity options\nawait onesignal.live_activities.setup_default()\n```\n\n---\n\n## Privacy \u0026 Consent\n\nFor GDPR and other privacy regulations, you can require user consent before collecting data:\n\n```python\n# Create OneSignal with consent requirement\nonesignal = fos.OneSignal(\n    app_id=ONESIGNAL_APP_ID,\n    require_consent=True,  # SDK won't collect data until consent is given\n)\n\n# After user accepts your privacy policy\nawait onesignal.consent_given(True)\n\n# If user declines\nawait onesignal.consent_given(False)\n```\n\n\u003e **Important:** `require_consent=True` must be set in the constructor for the consent methods to work.\n\u003e Without it, the SDK is fully active from initialization and calling `consent_given()` has no practical effect.\n\n---\n\n## Debugging\n\n### Log Levels\n\nConfigure SDK logging for development:\n\n```python\n# Set log level during initialization\nonesignal = fos.OneSignal(\n    app_id=ONESIGNAL_APP_ID,\n    log_level=fos.OSLogLevel.VERBOSE,\n)\n\n# Or change it dynamically\nawait onesignal.debug.set_log_level(fos.OSLogLevel.DEBUG)\n\n# Set alert level (visual alerts for errors)\nawait onesignal.debug.set_alert_level(fos.OSLogLevel.ERROR)\n```\n\n**Available log levels:**\n\n| Level | Description |\n|-------|-------------|\n| `NONE` | No logging |\n| `FATAL` | Only fatal errors |\n| `ERROR` | Errors and fatal errors |\n| `WARN` | Warnings and above |\n| `INFO` | Informational messages and above |\n| `DEBUG` | Debug messages and above |\n| `VERBOSE` | All messages including verbose details |\n\n### Error Handling\n\n```python\ndef on_error(e: fos.OSErrorEvent):\n    \"\"\"Called when an error occurs in the SDK.\"\"\"\n    print(f\"Error in {e.method}: {e.message}\")\n    if e.stack_trace:\n        print(f\"Stack trace: {e.stack_trace}\")\n\n\nonesignal = fos.OneSignal(\n    app_id=ONESIGNAL_APP_ID,\n    on_error=on_error,\n)\n```\n\n### Debug Console\n\nA built-in visual console for viewing application logs during development:\n\n```python\nimport flet as ft\nimport flet_onesignal as fos\n\n# Setup file-based logging (writes to FLET_APP_CONSOLE or debug.log)\nlogger = fos.setup_logging()\n\nasync def main(page: ft.Page):\n    debug_console = fos.DebugConsole()\n\n    page.appbar = ft.AppBar(\n        title=ft.Text(\"My App\"),\n        actions=[debug_console.icon],  # Bug icon opens the console\n    )\n\n    # Or use a floating action button instead\n    # page.floating_action_button = debug_console.fab\n\n    logger.info(\"App started\")\n    page.add(ft.Text(\"Hello World\"))\n\nft.run(main)\n```\n\nThe `DebugConsole` reads log entries written by `setup_logging()` and displays them in a filterable dialog with color-coded levels (`fos.LogLevel.DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`).\n\n### Android Logcat Scripts\n\nThe [`scripts/`](scripts/) directory includes two logcat viewer scripts that display Android logs with **Android Studio-style colors and formatting**. They auto-detect the focused app, filter by its PID, and highlight Flet/Flutter, Python errors and exceptions.\n\n**Bash version** (Linux/macOS — requires `adb` in PATH):\n\n```bash\n# Default filter (flutter, python, Error, Exception, Traceback)\n./scripts/flet_log.sh\n\n# Add extra filters\n./scripts/flet_log.sh \"OneSignal|Firebase\"\n```\n\n**Python version** (cross-platform):\n\n```bash\npython scripts/flet_log.py\n\npython scripts/flet_log.py \"OneSignal|Firebase\"\n```\n\n\u003e **Requirement:** A device or emulator connected via `adb`. The scripts clear the logcat buffer on each app restart so you only see fresh output.\n\n---\n\n## API Reference\n\n### OneSignal (Main Class)\n\n```python\nfos.OneSignal(\n    app_id: str,                          # Required: Your OneSignal App ID\n    log_level: OSLogLevel = None,         # Optional: SDK log level\n    visual_alert_level: OSLogLevel = None, # Optional: Visual alert level (iOS)\n    require_consent: bool = False,        # Optional: Require user consent\n    on_notification_click: Callable = None,\n    on_notification_foreground: Callable = None,\n    on_permission_change: Callable = None,\n    on_user_change: Callable = None,\n    on_push_subscription_change: Callable = None,\n    on_iam_click: Callable = None,\n    on_iam_will_display: Callable = None,\n    on_iam_did_display: Callable = None,\n    on_iam_will_dismiss: Callable = None,\n    on_iam_did_dismiss: Callable = None,\n    on_error: Callable = None,\n)\n```\n\n### Event Types\n\n| Event Class | Properties |\n|-------------|------------|\n| `OSNotificationClickEvent` | `notification`, `action_id` |\n| `OSNotificationWillDisplayEvent` | `notification`, `notification_id` |\n| `OSPermissionChangeEvent` | `permission` |\n| `OSUserChangedEvent` | `state.onesignal_id`, `state.external_id` |\n| `OSPushSubscriptionChangedEvent` | `id`, `token`, `opted_in` |\n| `OSInAppMessageClickEvent` | `message`, `result.action_id`, `result.url`, `result.url_target`, `result.closing_message` |\n| `OSInAppMessageWillDisplayEvent` | `message` |\n| `OSInAppMessageDidDisplayEvent` | `message` |\n| `OSInAppMessageWillDismissEvent` | `message` |\n| `OSInAppMessageDidDismissEvent` | `message` |\n| `OSErrorEvent` | `method`, `message`, `stack_trace` |\n\n### Enums\n\n```python\nclass OSLogLevel(Enum):\n    NONE = \"none\"\n    FATAL = \"fatal\"\n    ERROR = \"error\"\n    WARN = \"warn\"\n    INFO = \"info\"\n    DEBUG = \"debug\"\n    VERBOSE = \"verbose\"\n```\n\n---\n\n## Migration from v0.3.x\n\nIf upgrading from version 0.3.x, note these breaking changes:\n\n| v0.3.x (Old) | v0.4.0 (New) |\n|--------------|--------------|\n| `fos.OneSignalSettings(app_id=...)` | `fos.OneSignal(app_id=...)` |\n| `onesignal.get_onesignal_id()` | `await onesignal.user.get_onesignal_id()` |\n| `onesignal.get_external_user_id()` | `await onesignal.user.get_external_id()` |\n| `onesignal.login(id)` | `await onesignal.login(id)` |\n| `onesignal.logout()` | `await onesignal.logout()` |\n| `onesignal.set_language(code)` | `await onesignal.user.set_language(code)` |\n| `onesignal.add_alias(alias, id)` | `await onesignal.user.add_alias(label, id)` |\n| `onesignal.request_permission()` | `await onesignal.notifications.request_permission()` |\n| `onesignal.clear_all_notifications()` | `await onesignal.notifications.clear_all()` |\n| `on_notification_opened` | `on_notification_click` |\n| `on_notification_received` | `on_notification_foreground` |\n| `on_click_in_app_messages` | `on_iam_click` |\n| `ft.app(target=main)` | `ft.run(main)` |\n\n**Key changes:**\n- All methods are now **async-only** (no `_async` suffix)\n- Methods are organized into **sub-modules** (`.user`, `.notifications`, etc.)\n- Uses `ft.Service` base class instead of `Control`\n- New event types with structured data\n\n---\n\n## Troubleshooting\n\n### Notifications not appearing\n\n1. Verify your OneSignal App ID is correct\n2. Check that you've requested and received notification permission\n3. Ensure platform certificates (APNs/FCM) are configured in OneSignal dashboard\n4. Check device logs for any SDK errors\n\n### App crashes on startup\n\n1. Verify minimum SDK versions are met\n2. Check that the OneSignal is added to `page.services`\n3. Review the `on_error` handler for any initialization errors\n\n### Tags not syncing\n\n1. Tags are synced asynchronously - allow a few seconds\n2. Check your network connection\n3. Verify tags in the OneSignal dashboard under Users\n\n---\n\n## Example App\n\nA complete example demonstrating all features is available in the [`examples/flet_onesignal_example`](examples/flet_onesignal_example) directory.\n\nIt includes pages for each module — login, notifications, tags, aliases, in-app messages, location, session outcomes and more — built with Flet's declarative UI.\n\nTo run:\n\n    cd examples/flet_onesignal_example\n    uv sync\n    uv run python src/main.py\n\n---\n\n## 🌐 Community\n\nJoin the community to contribute or get help:\n\n- [Discord](https://discord.gg/dzWXP8SHG8)\n- [GitHub Issues](https://github.com/brunobrown/flet-asp/issues)\n\n## ⭐ Support\n\nIf you like this project, please give it a [GitHub star](https://github.com/brunobrown/flet-asp) ⭐\n\n---\n\n## 🤝 Contributing\n\nContributions and feedback are welcome!\n\n1. Fork the repository\n2. Create a feature branch\n3. Submit a pull request with detailed explanation\n\nFor feedback, [open an issue](https://github.com/brunobrown/flet-asp/issues) with your suggestions.\n\n---\n\n## Try **flet-onesignal** today and enhance your Flet apps with push notifications!\n\n---\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/user-attachments/assets/431aa05f-5fbc-4daa-9689-b9723583e25a\" width=\"50%\"\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003ca href=\"https://www.bible.com/bible/116/PRO.16.NLT\"\u003e Commit your work to the LORD, and your plans will succeed. Proverbs 16:3\u003c/a\u003e\u003c/p\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunobrown%2Fflet-onesignal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrunobrown%2Fflet-onesignal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunobrown%2Fflet-onesignal/lists"}