Skip to main content

Flutter OneSignal package integration for Python Flet.

Project description

Flet OneSignal

Flet OneSignal

OneSignal SDK integration for Flet applications

License Package version Supported Python versions PyPI Downloads


Overview

Flet OneSignal is an extension that integrates the OneSignal Flutter SDK with Flet applications. It provides a complete Python API for:

Version 0.4.0 - Built for Flet 0.80.x with a modular architecture that mirrors the OneSignal SDK structure.


Buy Me a Coffee

If you find this project useful, please consider supporting its development:

Buy Me a Coffee

Requirements

Component Minimum Version
Python 3.10+
Flet 0.80.x+

Platform Requirements

Platform Minimum Version Notes
iOS 12.0+ Requires Xcode 14+
Android API 24 (Android 7.0)+ Requires compileSdkVersion 33+

Installation

Step 1: Install the Package

Choose your preferred package manager:

# Using UV (Recommended)
uv add flet-onesignal

# Using pip
pip install flet-onesignal

# Using Poetry
poetry add flet-onesignal

Step 2: Configure pyproject.toml

Add the dependency to your project configuration:

[project]
name = "my-flet-app"
version = "1.0.0"
requires-python = ">=3.10"

dependencies = [
    "flet>=0.80.5",
    "flet-onesignal>=0.4.0",
]

[tool.flet.app]
path = "src"

Step 3: OneSignal Dashboard Setup (Android)

  1. Create an account at OneSignal.com, then click + Create > New App.

  2. Enter your App Name, select the organization, choose Google Android (FCM) as the channel, and click Next: Configure Your Platform.

    New OneSignal App

  3. Upload your Service Account JSON file. To generate it, go to the Firebase Console > Project Settings > Service accounts > Generate new private key. See the OneSignal Android credentials guide for detailed instructions. Click Save & Continue.

    FCM Configuration

  4. Select Flutter as the target SDK, then click Save & Continue.

    Select SDK

  5. Copy the App ID displayed on the screen and click Done. You will use this ID in your Flet app.

    App ID

Step 4: iOS Configuration

  1. Enable Push Notifications capability in Xcode
  2. Enable Background Modes > Remote notifications
  3. Add your APNs certificate to the OneSignal dashboard

Quick Start

import flet as ft
import flet_onesignal as fos

# Your OneSignal App ID from the dashboard
ONESIGNAL_APP_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"


async def main(page: ft.Page):
    page.title = "My App"

    # Initialize OneSignal
    onesignal = fos.OneSignal(
        app_id=ONESIGNAL_APP_ID,
        log_level=fos.OSLogLevel.DEBUG,  # Enable debug logging
    )

    # Add to page services (required for Flet 0.80.x services)
    page.services.append(onesignal)

    # Request notification permission
    permission_granted = await onesignal.notifications.request_permission()
    print(f"Notification permission: {permission_granted}")

    # Identify the user (optional but recommended)
    await onesignal.login("user_12345")

    page.add(ft.Text("OneSignal is ready!"))


if __name__ == "__main__":
    ft.run(main)

Architecture

The SDK follows a modular architecture that mirrors the official OneSignal SDK:

fos.OneSignal
│
├── .debug              # Logging and debugging
├── .user               # User identity, tags, aliases, email, SMS
├── .notifications      # Push notification management
├── .in_app_messages    # In-app message triggers and lifecycle
├── .location           # Location sharing (optional)
├── .session            # Outcomes and analytics
└── .live_activities    # iOS Live Activities (iOS 16.1+)

Each module provides focused functionality and can be accessed as a property of the main OneSignal instance.


User Management

Login and Logout

Associate users with their account in your system using an External User ID:

# Login - Associates the device with your user ID
await onesignal.login("user_12345")

# Logout - Removes the association, creates anonymous user
await onesignal.logout()

Best Practice: Call login() when the user signs into your app and logout() when they sign out.

Get User IDs

# Get the OneSignal-generated user ID
onesignal_id = await onesignal.user.get_onesignal_id()
print(f"OneSignal ID: {onesignal_id}")

# Get the External User ID (set via login)
external_id = await onesignal.user.get_external_id()
print(f"External ID: {external_id}")

Tags

Tags are key-value pairs used for segmentation and personalization:

# Add a single tag
await onesignal.user.add_tag("subscription_type", "premium")

# Add multiple tags at once
await onesignal.user.add_tags({
    "favorite_team": "barcelona",
    "notification_frequency": "daily",
    "app_version": "2.1.0",
})

# Remove a tag
await onesignal.user.remove_tag("old_tag")

# Remove multiple tags
await onesignal.user.remove_tags(["tag1", "tag2", "tag3"])

# Get all tags
tags = await onesignal.user.get_tags()
print(f"User tags: {tags}")

Aliases

Aliases allow you to associate multiple identifiers with a single user:

# Add an alias (e.g., CRM ID, database ID)
await onesignal.user.add_alias("crm_id", "CRM_98765")

# Add multiple aliases
await onesignal.user.add_aliases({
    "database_id": "DB_12345",
    "analytics_id": "GA_67890",
})

# Remove an alias
await onesignal.user.remove_alias("old_alias")

Email Subscriptions

Add email addresses for omnichannel messaging:

# Add an email subscription
await onesignal.user.add_email("user@example.com")

# Remove an email subscription
await onesignal.user.remove_email("user@example.com")

SMS Subscriptions

Add phone numbers for SMS messaging (use E.164 format):

# Add SMS subscription (E.164 format: +[country code][number])
await onesignal.user.add_sms("+5511999999999")

# Remove SMS subscription
await onesignal.user.remove_sms("+5511999999999")

Language

Set the user's preferred language for localized notifications:

# Set language using ISO 639-1 code
await onesignal.user.set_language("pt")  # Portuguese
await onesignal.user.set_language("es")  # Spanish
await onesignal.user.set_language("en")  # English

# You can also use the Language enum for auto-complete support
await onesignal.user.set_language(fos.Language.PORTUGUESE.value)
await onesignal.user.set_language(fos.Language.SPANISH.value)

Push Notifications

Requesting Permission

You must request permission before sending push notifications:

# Request permission with fallback to settings
granted = await onesignal.notifications.request_permission(
    fallback_to_settings=True  # Opens settings if previously denied
)

if granted:
    print("User granted notification permission!")
else:
    print("User denied notification permission")

Check Permission Status

# Check if permission can be requested (not yet prompted)
can_request = await onesignal.notifications.can_request_permission()

# Check current permission status
has_permission = await onesignal.notifications.get_permission()

iOS Provisional Authorization

Request provisional (quiet) authorization on iOS 12+:

# Notifications will be delivered quietly to Notification Center
authorized = await onesignal.notifications.register_for_provisional_authorization()

Managing Notifications

# Clear all notifications from the notification center
await onesignal.notifications.clear_all()

# Remove a specific notification (Android only)
await onesignal.notifications.remove_notification(notification_id)

# Remove a group of notifications (Android only)
await onesignal.notifications.remove_grouped_notifications("group_key")

Foreground Display Control

Control whether notifications are shown when the app is in the foreground:

# Inside on_notification_foreground handler:
# Prevent a notification from being displayed
await onesignal.notifications.prevent_default(e.notification_id)

# Later, allow display if needed
await onesignal.notifications.display(e.notification_id)

Push Subscription Control

# Opt user into push notifications
await onesignal.user.opt_in_push()

# Opt user out of push notifications
await onesignal.user.opt_out_push()

# Check if user is opted in
is_opted_in = await onesignal.user.is_push_opted_in()

# Get push subscription details
subscription_id = await onesignal.user.get_push_subscription_id()
push_token = await onesignal.user.get_push_subscription_token()

Handling Notification Events

def on_notification_click(e: fos.OSNotificationClickEvent):
    """Called when user taps on a notification."""
    print(f"Notification clicked: {e.notification}")
    print(f"Action ID: {e.action_id}")  # If action buttons were used


def on_notification_foreground(e: fos.OSNotificationWillDisplayEvent):
    """Called when notification received while app is in foreground."""
    print(f"Notification received: {e.notification}")
    print(f"Notification ID: {e.notification_id}")

    # Optionally prevent display and handle manually:
    # await onesignal.notifications.prevent_default(e.notification_id)
    # Later, allow display if needed:
    # await onesignal.notifications.display(e.notification_id)


def on_permission_change(e: fos.OSPermissionChangeEvent):
    """Called when notification permission status changes."""
    print(f"Permission granted: {e.permission}")


# Register handlers when creating OneSignal instance
onesignal = fos.OneSignal(
    app_id=ONESIGNAL_APP_ID,
    on_notification_click=on_notification_click,
    on_notification_foreground=on_notification_foreground,
    on_permission_change=on_permission_change,
)

In-App Messages

In-App Messages (IAMs) are messages displayed within your app based on triggers.

Triggers

Triggers determine when IAMs are displayed:

# Add a trigger
await onesignal.in_app_messages.add_trigger("level_completed", "5")

# Add multiple triggers
await onesignal.in_app_messages.add_triggers({
    "screen": "checkout",
    "cart_value": "50",
})

# Remove a trigger
await onesignal.in_app_messages.remove_trigger("old_trigger")

# Remove multiple triggers
await onesignal.in_app_messages.remove_triggers(["trigger1", "trigger2"])

# Clear all triggers
await onesignal.in_app_messages.clear_triggers()

Pausing In-App Messages

Temporarily prevent IAMs from displaying:

# Pause IAM display
await onesignal.in_app_messages.pause()

# Resume IAM display
await onesignal.in_app_messages.resume()

# Check if paused
is_paused = await onesignal.in_app_messages.is_paused()

IAM Event Handlers

def on_iam_click(e: fos.OSInAppMessageClickEvent):
    """Called when user interacts with an IAM."""
    print(f"IAM clicked - Action: {e.result.action_id}")
    print(f"URL: {e.result.url}")
    print(f"Closing message: {e.result.closing_message}")


def on_iam_will_display(e: fos.OSInAppMessageWillDisplayEvent):
    """Called before an IAM is displayed."""
    print(f"IAM will display: {e.message}")


def on_iam_did_display(e: fos.OSInAppMessageDidDisplayEvent):
    """Called after an IAM is displayed."""
    print("IAM displayed")


def on_iam_will_dismiss(e: fos.OSInAppMessageWillDismissEvent):
    """Called before an IAM is dismissed."""
    print("IAM will dismiss")


def on_iam_did_dismiss(e: fos.OSInAppMessageDidDismissEvent):
    """Called after an IAM is dismissed."""
    print("IAM dismissed")


onesignal = fos.OneSignal(
    app_id=ONESIGNAL_APP_ID,
    on_iam_click=on_iam_click,
    on_iam_will_display=on_iam_will_display,
    on_iam_did_display=on_iam_did_display,
    on_iam_will_dismiss=on_iam_will_dismiss,
    on_iam_did_dismiss=on_iam_did_dismiss,
)

Location

Share user location for geo-targeted messaging:

# Request location permission
granted = await onesignal.location.request_permission()

# Enable location sharing
await onesignal.location.set_shared(True)

# Disable location sharing
await onesignal.location.set_shared(False)

# Check if location is being shared
is_shared = await onesignal.location.is_shared()

Note: Location sharing requires appropriate permissions configured in your app.


Outcomes

Track user actions and conversions attributed to notifications:

# Track a simple outcome
await onesignal.session.add_outcome("product_viewed")

# Track a unique outcome (counted once per notification)
await onesignal.session.add_unique_outcome("app_opened")

# Track an outcome with a value (e.g., purchase amount)
await onesignal.session.add_outcome_with_value("purchase", 29.99)

Live Activities (iOS)

Update iOS Live Activities in real-time (iOS 16.1+):

# Enter a Live Activity
await onesignal.live_activities.enter(
    activity_id="delivery_12345",
    token="live_activity_push_token"
)

# Exit a Live Activity
await onesignal.live_activities.exit("delivery_12345")

# Set push-to-start token for a Live Activity type
await onesignal.live_activities.set_push_to_start_token(
    activity_type="DeliveryActivityAttributes",
    token="push_to_start_token"
)

# Remove push-to-start token
await onesignal.live_activities.remove_push_to_start_token("DeliveryActivityAttributes")

# Setup default Live Activity options
await onesignal.live_activities.setup_default()

Privacy & Consent

For GDPR and other privacy regulations, you can require user consent before collecting data:

# Create OneSignal with consent requirement
onesignal = fos.OneSignal(
    app_id=ONESIGNAL_APP_ID,
    require_consent=True,  # SDK won't collect data until consent is given
)

# After user accepts your privacy policy
await onesignal.consent_given(True)

# If user declines
await onesignal.consent_given(False)

Debugging

Log Levels

Configure SDK logging for development:

# Set log level during initialization
onesignal = fos.OneSignal(
    app_id=ONESIGNAL_APP_ID,
    log_level=fos.OSLogLevel.VERBOSE,
)

# Or change it dynamically
await onesignal.debug.set_log_level(fos.OSLogLevel.DEBUG)

# Set alert level (visual alerts for errors)
await onesignal.debug.set_alert_level(fos.OSLogLevel.ERROR)

Available log levels:

Level Description
NONE No logging
FATAL Only fatal errors
ERROR Errors and fatal errors
WARN Warnings and above
INFO Informational messages and above
DEBUG Debug messages and above
VERBOSE All messages including verbose details

Error Handling

def on_error(e: fos.OSErrorEvent):
    """Called when an error occurs in the SDK."""
    print(f"Error in {e.method}: {e.message}")
    if e.stack_trace:
        print(f"Stack trace: {e.stack_trace}")


onesignal = fos.OneSignal(
    app_id=ONESIGNAL_APP_ID,
    on_error=on_error,
)

Debug Console

A built-in visual console for viewing application logs during development:

import flet as ft
import flet_onesignal as fos

# Setup file-based logging (writes to FLET_APP_CONSOLE or debug.log)
logger = fos.setup_logging()

async def main(page: ft.Page):
    debug_console = fos.DebugConsole()

    page.appbar = ft.AppBar(
        title=ft.Text("My App"),
        actions=[debug_console.icon],  # Bug icon opens the console
    )

    # Or use a floating action button instead
    # page.floating_action_button = debug_console.fab

    logger.info("App started")
    page.add(ft.Text("Hello World"))

ft.run(main)

The 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).

Android Logcat Scripts

The 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.

Bash version (Linux/macOS — requires adb in PATH):

# Default filter (flutter, python, Error, Exception, Traceback)
./scripts/flet_log.sh

# Add extra filters
./scripts/flet_log.sh "OneSignal|Firebase"

Python version (cross-platform):

python scripts/flet_log.py

python scripts/flet_log.py "OneSignal|Firebase"

Requirement: A device or emulator connected via adb. The scripts clear the logcat buffer on each app restart so you only see fresh output.


API Reference

OneSignal (Main Class)

fos.OneSignal(
    app_id: str,                          # Required: Your OneSignal App ID
    log_level: OSLogLevel = None,         # Optional: SDK log level
    visual_alert_level: OSLogLevel = None, # Optional: Visual alert level (iOS)
    require_consent: bool = False,        # Optional: Require user consent
    on_notification_click: Callable = None,
    on_notification_foreground: Callable = None,
    on_permission_change: Callable = None,
    on_user_change: Callable = None,
    on_push_subscription_change: Callable = None,
    on_iam_click: Callable = None,
    on_iam_will_display: Callable = None,
    on_iam_did_display: Callable = None,
    on_iam_will_dismiss: Callable = None,
    on_iam_did_dismiss: Callable = None,
    on_error: Callable = None,
)

Event Types

Event Class Properties
OSNotificationClickEvent notification, action_id
OSNotificationWillDisplayEvent notification, notification_id
OSPermissionChangeEvent permission
OSUserChangedEvent state.onesignal_id, state.external_id
OSPushSubscriptionChangedEvent id, token, opted_in
OSInAppMessageClickEvent message, result.action_id, result.url, result.url_target, result.closing_message
OSInAppMessageWillDisplayEvent message
OSInAppMessageDidDisplayEvent message
OSInAppMessageWillDismissEvent message
OSInAppMessageDidDismissEvent message
OSErrorEvent method, message, stack_trace

Enums

class OSLogLevel(Enum):
    NONE = "none"
    FATAL = "fatal"
    ERROR = "error"
    WARN = "warn"
    INFO = "info"
    DEBUG = "debug"
    VERBOSE = "verbose"

Migration from v0.3.x

If upgrading from version 0.3.x, note these breaking changes:

v0.3.x (Old) v0.4.0 (New)
fos.OneSignalSettings(app_id=...) fos.OneSignal(app_id=...)
onesignal.get_onesignal_id() await onesignal.user.get_onesignal_id()
onesignal.get_external_user_id() await onesignal.user.get_external_id()
onesignal.login(id) await onesignal.login(id)
onesignal.logout() await onesignal.logout()
onesignal.set_language(code) await onesignal.user.set_language(code)
onesignal.add_alias(alias, id) await onesignal.user.add_alias(label, id)
onesignal.request_permission() await onesignal.notifications.request_permission()
onesignal.clear_all_notifications() await onesignal.notifications.clear_all()
on_notification_opened on_notification_click
on_notification_received on_notification_foreground
on_click_in_app_messages on_iam_click
ft.app(target=main) ft.run(main)

Key changes:

  • All methods are now async-only (no _async suffix)
  • Methods are organized into sub-modules (.user, .notifications, etc.)
  • Uses ft.Service base class instead of Control
  • New event types with structured data

Troubleshooting

Notifications not appearing

  1. Verify your OneSignal App ID is correct
  2. Check that you've requested and received notification permission
  3. Ensure platform certificates (APNs/FCM) are configured in OneSignal dashboard
  4. Check device logs for any SDK errors

App crashes on startup

  1. Verify minimum SDK versions are met
  2. Check that the OneSignal is added to page.services
  3. Review the on_error handler for any initialization errors

Tags not syncing

  1. Tags are synced asynchronously - allow a few seconds
  2. Check your network connection
  3. Verify tags in the OneSignal dashboard under Users

Example App

A complete example demonstrating all features is available in the examples/flet_onesignal_example directory.

It includes pages for each module — login, notifications, tags, aliases, in-app messages, location, session outcomes and more — built with Flet's declarative UI.

To run:

cd examples/flet_onesignal_example
uv sync
uv run python src/main.py

🌐 Community

Join the community to contribute or get help:

⭐ Support

If you like this project, please give it a GitHub star


🤝 Contributing

Contributions and feedback are welcome!

  1. Fork the repository
  2. Create a feature branch
  3. Submit a pull request with detailed explanation

For feedback, open an issue with your suggestions.


Try flet-onesignal today and enhance your Flet apps with push notifications!


Commit your work to the LORD, and your plans will succeed. Proverbs 16:3

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flet_onesignal-0.4.0.tar.gz (44.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flet_onesignal-0.4.0-py3-none-any.whl (42.5 kB view details)

Uploaded Python 3

File details

Details for the file flet_onesignal-0.4.0.tar.gz.

File metadata

  • Download URL: flet_onesignal-0.4.0.tar.gz
  • Upload date:
  • Size: 44.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for flet_onesignal-0.4.0.tar.gz
Algorithm Hash digest
SHA256 a76dfdb5911ac41bbd1ea1c71f131e773ff642cc85f4258ba243b17319d3a2bc
MD5 5ae62394ffde461a465746092ba1134f
BLAKE2b-256 18a75c416de551ef0dcc18e06f58d71d3bee02bd0e180bb203d1b39b3fbdc08a

See more details on using hashes here.

File details

Details for the file flet_onesignal-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: flet_onesignal-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 42.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for flet_onesignal-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 185f7f05455ff917f9d92bd349eb5391e92ed68a997969fc86a8c783fd9d30cc
MD5 5345700fa055ff8afcde56c21bf12839
BLAKE2b-256 d3669faa4dd3dc608fec95ecf78effda93b3760946d5973b32e22400e4610be8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page