├── .python-version
├── src
├── flutter
│ └── flet_onesignal
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── lib
│ │ ├── flet_onesignal.dart
│ │ └── src
│ │ │ ├── utils
│ │ │ └── funcs.dart
│ │ │ ├── create_control.dart
│ │ │ └── flet_onesignal.dart
│ │ ├── .gitignore
│ │ ├── LICENSE
│ │ └── pubspec.yaml
└── flet_onesignal
│ ├── __init__.py
│ ├── languages.py
│ └── flet_onesignal.py
├── examples
└── flet_onesignal_example
│ ├── pyproject.toml
│ └── src
│ └── main.py
├── LICENSE
├── pyproject.toml
├── .gitignore
└── README.md
/.python-version:
--------------------------------------------------------------------------------
1 | 3.12.4
2 |
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.1
2 |
3 | * TODO: Describe initial release.
4 |
--------------------------------------------------------------------------------
/src/flet_onesignal/__init__.py:
--------------------------------------------------------------------------------
1 | from flet_onesignal.flet_onesignal import OneSignal, OneSignalSettings
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/README.md:
--------------------------------------------------------------------------------
1 | # Flet OneSignal
2 |
3 | ---
4 |
5 | ### Flutter OneSignal package integration for Python Flet
6 |
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/lib/flet_onesignal.dart:
--------------------------------------------------------------------------------
1 | library flet_onesignal;
2 |
3 | export '../src/create_control.dart' show createControl, ensureInitialized;
--------------------------------------------------------------------------------
/examples/flet_onesignal_example/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "flet-onesignal-example"
3 | version = "0.1.0"
4 | description = "flet-onesignal-example"
5 | readme = "README.md"
6 | requires-python = ">=3.12"
7 | authors = [
8 | { name = "developer", email = "you@example.com" }
9 | ]
10 |
11 | dependencies = [
12 | "flet>=0.26.0",
13 | "flet-onesignal>=0.2.0",
14 | ]
15 |
16 | [tool.uv]
17 | dev-dependencies = [
18 | "flet[all]>=0.26.0",
19 | ]
20 |
21 | [tool.poetry.group.dev.dependencies]
22 | flet = {extras = ["all"], version = "0.26.0"}
23 |
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/lib/src/utils/funcs.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'package:flutter/material.dart';
3 |
4 |
5 | void handleFlutterError({
6 | required String method,
7 | required Object error,
8 | required StackTrace stackTrace,
9 | required void Function(String, String, String) trigger,
10 | required String controlId,
11 | }) {
12 | debugPrint("❌ Error in $method:\n$error\n$stackTrace");
13 |
14 | trigger(
15 | controlId,
16 | "error",
17 | jsonEncode({
18 | "method": method,
19 | "message": error.toString(),
20 | "stackTrace": stackTrace.toString(),
21 | }),
22 | );
23 | }
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/lib/src/create_control.dart:
--------------------------------------------------------------------------------
1 | import 'package:flet/flet.dart';
2 | import 'flet_onesignal.dart';
3 |
4 |
5 | CreateControlFactory createControl = (CreateControlArgs args) {
6 | switch (args.control.type) {
7 | case 'flet_onesignal':
8 | if (args.parent == null) {
9 | throw ArgumentError('Parent cannot be null');
10 | }
11 | return FletOneSignalControl(
12 | parent: args.parent!, // Força a não nulidade após a verificação
13 | control: args.control,
14 | backend: args.backend
15 | );
16 | default:
17 | return null;
18 | }
19 | };
20 |
21 | void ensureInitialized() {
22 | // Required initializations, if any
23 | // Se houver inicializações necessárias
24 | }
25 |
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26 | /pubspec.lock
27 | **/doc/api/
28 | .dart_tool/
29 | build/
30 | .flutter-plugins
31 | .flutter-plugins-dependencies
32 |
33 | # FVM Version Cache
34 | .fvm/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Bruno Brown
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.
22 |
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Bruno Brown
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.
22 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "flet-onesignal"
3 | version = "0.3.3"
4 | description = "Flutter OneSignal package integration for Python Flet."
5 | requires-python = ">=3.9"
6 | readme = "README.md"
7 | authors = [
8 | { name = "brunobrown", email = "brunobrown.86@gmail.com" }
9 | ]
10 | license = "MIT"
11 | license-files = ["LICENSE"]
12 |
13 | dependencies = [
14 | "flet>=0.26.0",
15 | ]
16 |
17 | classifiers = [
18 | "Topic :: Software Development :: Debuggers",
19 | "Intended Audience :: Developers",
20 | "Natural Language :: English",
21 | "Natural Language :: Portuguese (Brazilian)",
22 | "Operating System :: OS Independent",
23 | "Development Status :: 5 - Production/Stable",
24 | "Programming Language :: Python :: 3.9",
25 | "Programming Language :: Python :: 3.10",
26 | "Programming Language :: Python :: 3.11",
27 | "Programming Language :: Python :: 3.12",
28 | "Programming Language :: Python :: 3.13",
29 | ]
30 |
31 | [project.urls]
32 | Homepage = "https://github.com/brunobrown/flet-onesignal"
33 | #Documentation = ""
34 | Repository = "https://github.com/brunobrown/flet-onesignal"
35 | Issues = "https://github.com/brunobrown/flet-onesignal/issues"
36 | #Changelog = ""
37 |
38 | [tool.uv]
39 | dev-dependencies = [
40 | "flet[all]>=0.26.0",
41 | ]
42 |
43 | #[tool.setuptools]
44 | ##packages = ["flet_onesignal"]
45 | ##package-dir = {"" = "src"}
46 |
47 | [tool.setuptools.package-data]
48 | "flutter.flet_onesignal" = ["**/*"]
49 |
50 | [build-system]
51 | requires = ["setuptools"]
52 | build-backend = "setuptools.build_meta"
53 |
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flet_onesignal
2 | description: "Flutter OneSignal package integration for Python Flet."
3 | version: 0.0.2
4 | #homepage: https://github.com/brunobrown/onesignal-flet
5 | repository: https://github.com/brunobrown/flet-onesignal
6 |
7 | environment:
8 | sdk: ^3.5.2
9 | flutter: ">=1.17.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 |
15 | flet: ^0.26.0
16 | onesignal_flutter: ^5.3.2
17 |
18 | dev_dependencies:
19 | flutter_test:
20 | sdk: flutter
21 | flutter_lints: ^4.0.0
22 |
23 | # For information on the generic Dart part of this file, see the
24 | # following page: https://dart.dev/tools/pub/pubspec
25 |
26 | # The following section is specific to Flutter packages.
27 | flutter:
28 |
29 | # To add assets to your package, add an assets section, like this:
30 | # assets:
31 | # - images/a_dot_burr.jpeg
32 | # - images/a_dot_ham.jpeg
33 | #
34 | # For details regarding assets in packages, see
35 | # https://flutter.dev/to/asset-from-package
36 | #
37 | # An image asset can refer to one or more resolution-specific "variants", see
38 | # https://flutter.dev/to/resolution-aware-images
39 |
40 | # To add custom fonts to your package, add a fonts section here,
41 | # in this "flutter" section. Each entry in this list should have a
42 | # "family" key with the font family name, and a "fonts" key with a
43 | # list giving the asset and other descriptors for the font. For
44 | # example:
45 | # fonts:
46 | # - family: Schyler
47 | # fonts:
48 | # - asset: fonts/Schyler-Regular.ttf
49 | # - asset: fonts/Schyler-Italic.ttf
50 | # style: italic
51 | # - family: Trajan Pro
52 | # fonts:
53 | # - asset: fonts/TrajanPro.ttf
54 | # - asset: fonts/TrajanPro_Bold.ttf
55 | # weight: 700
56 | #
57 | # For details regarding fonts in packages, see
58 | # https://flutter.dev/to/font-from-package
59 |
--------------------------------------------------------------------------------
/src/flet_onesignal/languages.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | class Language(Enum):
4 | """
5 | Enumeração de línguas suportadas pelo OneSignal.
6 | """
7 |
8 | ENGLISH = "en"
9 | SPANISH = "es"
10 | PORTUGUESE = "pt"
11 | FRENCH = "fr"
12 | GERMAN = "de"
13 | ITALIAN = "it"
14 | RUSSIAN = "ru"
15 | CHINESE_SIMPLIFIED = "zh-Hans"
16 | CHINESE_TRADITIONAL = "zh-Hant"
17 | JAPANESE = "ja"
18 | KOREAN = "ko"
19 | ARABIC = "ar"
20 | HINDI = "hi"
21 | TURKISH = "tr"
22 | DUTCH = "nl"
23 | SWEDISH = "sv"
24 | FINNISH = "fi"
25 | DANISH = "da"
26 | NORWEGIAN = "no"
27 | POLISH = "pl"
28 | CZECH = "cs"
29 | GREEK = "el"
30 | HUNGARIAN = "hu"
31 | ROMANIAN = "ro"
32 | THAI = "th"
33 | VIETNAMESE = "vi"
34 | INDONESIAN = "id"
35 | MALAY = "ms"
36 | FILIPINO = "tl"
37 | SWAHILI = "sw"
38 | URDU = "ur"
39 | PERSIAN = "fa"
40 | BENGALI = "bn"
41 | TAMIL = "ta"
42 | TELUGU = "te"
43 | MARATHI = "mr"
44 | GUJARATI = "gu"
45 | KANNADA = "kn"
46 | MALAYALAM = "ml"
47 | PUNJABI = "pa"
48 | SINHALA = "si"
49 | NEPALI = "ne"
50 | BURMESE = "my"
51 | KHMER = "km"
52 | LAO = "lo"
53 | MONGOLIAN = "mn"
54 | TIBETAN = "bo"
55 | UZBEK = "uz"
56 | KAZAKH = "kk"
57 | AZERBAIJANI = "az"
58 | GEORGIAN = "ka"
59 | ARMENIAN = "hy"
60 | HEBREW = "he"
61 | YIDDISH = "yi"
62 | ESPERANTO = "eo"
63 | LATIN = "la"
64 | BASQUE = "eu"
65 | CATALAN = "ca"
66 | GALICIAN = "gl"
67 | WELSH = "cy"
68 | IRISH = "ga"
69 | SCOTTISH_GAELIC = "gd"
70 | BRETON = "br"
71 | CORSICAN = "co"
72 | ALBANIAN = "sq"
73 | BOSNIAN = "bs"
74 | CROATIAN = "hr"
75 | SERBIAN = "sr"
76 | MACEDONIAN = "mk"
77 | BULGARIAN = "bg"
78 | SLOVAK = "sk"
79 | SLOVENIAN = "sl"
80 | ESTONIAN = "et"
81 | LATVIAN = "lv"
82 | LITHUANIAN = "lt"
83 | BELARUSIAN = "be"
84 | UKRAINIAN = "uk"
85 | KURDISH = "ku"
86 | TATAR = "tt"
87 | CHECHEN = "ce"
88 | UDMURT = "udm"
89 | KOMI = "kv"
90 | MARI = "chm"
91 | MORDVIN = "mdf"
92 | SAAMI = "se"
93 | FAROESE = "fo"
94 | GREENLANDIC = "kl"
95 | HAWAIIAN = "haw"
96 | MAORI = "mi"
97 | SAMOAN = "sm"
98 | TAHITIAN = "ty"
99 | TONGAN = "to"
100 | FIJIAN = "fj"
101 | MARSHALLESE = "mh"
102 | CHAMORRO = "ch"
103 | PALAUAN = "pau"
104 | KIRIBATI = "gil"
105 | NAURUAN = "na"
106 | TUVALUAN = "tvl"
107 | TOKELAUAN = "tkl"
108 | NIUEAN = "niu"
109 | COOK_ISLANDS_MAORI = "rar"
110 | HAITIAN_CREOLE = "ht"
111 | PAPIAMENTO = "pap"
112 | GUARANI = "gn"
113 | AYMARA = "ay"
114 | QUECHUA = "qu"
115 | MAPUDUNGUN = "arn"
116 | TUPI = "tpw"
117 | XHOSA = "xh"
118 | ZULU = "zu"
119 | AFRIKAANS = "af"
120 | SESOTHO = "st"
121 | TSWANA = "tn"
122 | VENDA = "ve"
123 | TSONGA = "ts"
124 | SWAZI = "ss"
125 | NDEBELE = "nr"
126 | SHONA = "sn"
127 | LUGANDA = "lg"
128 | KINYARWANDA = "rw"
129 | RUNDI = "rn"
130 | SOMALI = "so"
131 | OROMO = "om"
132 | AMHARIC = "am"
133 | TIGRINYA = "ti"
134 | WOLOF = "wo"
135 | FULA = "ff"
136 | MANDINKA = "mnk"
137 | MANDE = "man"
138 | KPELLE = "kpe"
139 | VAI = "vai"
140 | BASSA = "bsq"
141 | GREBO = "grb"
142 | KRU = "kro"
143 | GOLA = "gol"
144 | KISSI = "kis"
145 | LIMBA = "lia"
146 | KONO = "kno"
147 | KURANKO = "kur"
148 | LOKO = "lok"
149 | MANINKA = "mnk"
150 | MANDINGO = "man"
151 | SONINKE = "snk"
152 | SUSU = "sus"
153 | YALUNKA = "yal"
154 | BAMBARA = "bam"
155 | DYULA = "dyu"
156 | MALINKE = "mlq"
157 | MANJAK = "mjk"
158 | PAPEL = "pck"
159 | BALANTA = "blt"
160 | BIJAGO = "bjg"
161 | BUJUBU = "bjb"
162 | CRIOULO = "cri"
163 | FULFULDE = "fuv"
164 | HAUSA = "hau"
165 | IGBO = "ibo"
166 | YORUBA = "yor"
167 | EDO = "bin"
168 | EFIK = "efi"
169 | IGBIRA = "igb"
170 | IDOMA = "idu"
171 | ITSEKIRI = "its"
172 | IZON = "ijc"
173 | OKRIKA = "okr"
174 | URHOBO = "urh"
175 | ISOKO = "iso"
176 | KALABARI = "ijn"
177 |
--------------------------------------------------------------------------------
/examples/flet_onesignal_example/src/main.py:
--------------------------------------------------------------------------------
1 | import flet as ft
2 | import flet_onesignal as fos
3 | from functools import partial
4 |
5 | ONESIGNAL_APP_ID = "example-123a-12a3-1a23-abcd1ef23g45"
6 |
7 |
8 | async def main(page: ft.Page):
9 | page.appbar = ft.AppBar(title=ft.Text("OneSignal Test"), bgcolor=ft.Colors.BLUE_700, color=ft.Colors.WHITE)
10 | get_onesignal_id = ft.TextField(label='Get OneSignal ID', read_only=True)
11 | get_external_id = ft.TextField(label='Get External User ID', read_only=True, ignore_pointers=True)
12 | set_external_id = ft.TextField(label='Set External User ID', hint_text='User ID')
13 | language = ft.TextField(label='Language', hint_text='Language Code (en)', value='en', color=ft.Colors.GREEN)
14 |
15 | def handle_notification_opened(e):
16 | #Access the data of the clicked notification
17 | list_view.content.controls.append(ft.Text(f"Notification opened: {e.notification_opened}"))
18 | list_view.update()
19 |
20 | def handle_notification_received(e):
21 | # Access the data of the received notification
22 | list_view.content.controls.append(ft.Text(f"Notification received: {e.notification_received}"))
23 | list_view.update()
24 |
25 | def handle_click_in_app_messages(e):
26 | # Access the data of the received notification in app messages
27 | list_view.content.controls.append(ft.Text(f"Notification click_in_app_messages: {e.click_in_app_messages}"))
28 | list_view.update()
29 |
30 | def get_id(e):
31 | result = onesignal.get_onesignal_id()
32 | get_onesignal_id.value = result
33 | get_onesignal_id.update()
34 |
35 | def get_external_user_id(e):
36 | result = onesignal.get_external_user_id()
37 | get_external_id.value = result
38 | get_external_id.update()
39 |
40 | def handle_login(e, external_user_id):
41 | message = "Login failed"
42 |
43 | if not external_user_id.value:
44 | message = "Please enter external user ID"
45 |
46 | if external_user_id.value:
47 | result = onesignal.login(external_user_id.value)
48 | if result:
49 | message = "Login successful"
50 |
51 | list_view.content.controls.append(ft.Text(message))
52 | list_view.update()
53 |
54 | def handle_logout(e):
55 | onesignal.logout()
56 | set_external_id.value = None
57 | set_external_id.update()
58 |
59 | def set_language(e, language_code):
60 | result = onesignal.set_language(language_code.value)
61 | list_view.content.controls.append(ft.Text(result))
62 | list_view.update()
63 | print(result)
64 |
65 | def handle_error(e):
66 | #handle_error
67 | list_view.content.controls.append(ft.Text(f"Error: {e.data}"))
68 | list_view.update()
69 |
70 | onesignal = fos.OneSignal(
71 | settings=fos.OneSignalSettings(app_id=ONESIGNAL_APP_ID),
72 | on_notification_opened=handle_notification_opened,
73 | on_notification_received=handle_notification_received,
74 | on_click_in_app_messages=handle_click_in_app_messages,
75 | on_error=handle_error,
76 | )
77 |
78 | container = ft.Container(
79 | alignment=ft.alignment.bottom_center,
80 | content=ft.Row(
81 | scroll=ft.ScrollMode.ADAPTIVE,
82 | expand=True,
83 | vertical_alignment=ft.CrossAxisAlignment.CENTER,
84 | controls=[
85 | ft.ElevatedButton(
86 | text='Get OneSignal Id',
87 | on_click=get_id
88 | ),
89 | ft.ElevatedButton(
90 | 'Get External User Id',
91 | on_click=get_external_user_id
92 | ),
93 | ft.ElevatedButton(
94 | text='Set External User Id',
95 | on_click=partial(handle_login, external_user_id=set_external_id)
96 | ),
97 | ft.ElevatedButton(
98 | text='Logout External User Id',
99 | on_click=handle_logout
100 | ),
101 | ft.ElevatedButton(
102 | text='Set Language',
103 | on_click=partial(set_language, language_code=language)
104 | ),
105 | ]
106 | )
107 | )
108 |
109 | list_view = ft.Container(
110 | expand=True,
111 | content=ft.ListView(
112 | padding=ft.padding.all(10),
113 | spacing=5,
114 | )
115 | )
116 | page.overlay.append(onesignal)
117 |
118 | page.add(
119 | # onesignal,
120 | list_view,
121 | get_onesignal_id,
122 | get_external_id,
123 | set_external_id,
124 | language,
125 | container,
126 | )
127 |
128 |
129 | if __name__ == "__main__":
130 | ft.app(target=main)
131 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | *.tmp
7 | *.lock
8 | .DS_Store
9 | .atom/
10 | .buildlog/
11 | .history
12 | .svn/
13 | migrate_working_dir/
14 |
15 | # IntelliJ-related files
16 | *.iml
17 | *.ipr
18 | *.iws
19 | .idea/
20 |
21 | # Visual Studio Code-related files
22 | # Uncomment the following line if you wish to ignore VS Code configurations.
23 | #.vscode/
24 | .vscode/
25 |
26 | # Flutter/Dart/Pub-related files
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub/
32 | .pub-cache/
33 | pubspec.lock
34 | flutter_export_environment.sh
35 | **/doc/api/
36 |
37 | # Build outputs
38 | build/
39 |
40 | # Android-specific files
41 | android/.idea
42 | android/.cxx/
43 | android/local.properties
44 |
45 | # iOS-specific files
46 | ios/Flutter/.last_build_id
47 | ios/Flutter/App.framework
48 | ios/Flutter/Flutter.framework
49 | ios/Flutter/Flutter.podspec
50 | ios/ServiceDefinitions.json
51 | ios/Runner/GeneratedPluginRegistrant.*
52 |
53 | # macOS-specific files
54 | macos/Flutter/ephemeral/
55 |
56 | # Web-related files
57 | web/.dart_tool/
58 |
59 | # Windows-specific files
60 | windows/flutter/ephemeral/
61 |
62 | # Linux-specific files
63 | linux/flutter/ephemeral/
64 |
65 | # Generated metadata
66 | .metadata
67 |
68 | # Python ###
69 | # Byte-compiled / optimized / DLL files
70 | __pycache__/
71 | *.py[cod]
72 | *$py.class
73 |
74 | # C extensions
75 | *.so
76 |
77 | # Distribution / packaging
78 | .Python
79 | #build/
80 | develop-eggs/
81 | dist/
82 | downloads/
83 | eggs/
84 | .eggs/
85 | #lib/
86 | lib64/
87 | parts/
88 | sdist/
89 | var/
90 | wheels/
91 | share/python-wheels/
92 | *.egg-info/
93 | .installed.cfg
94 | *.egg
95 | MANIFEST
96 |
97 | # PyInstaller
98 | # Usually these files are written by a python script from a template
99 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
100 | *.manifest
101 | *.spec
102 |
103 | # Installer logs
104 | pip-log.txt
105 | pip-delete-this-directory.txt
106 |
107 | # Unit test / coverage reports
108 | htmlcov/
109 | .tox/
110 | .nox/
111 | .coverage
112 | .coverage.*
113 | .cache
114 | nosetests.xml
115 | coverage.xml
116 | *.cover
117 | *.py,cover
118 | .hypothesis/
119 | .pytest_cache/
120 | cover/
121 |
122 | # Translations
123 | *.mo
124 | *.pot
125 |
126 | # Django stuff:
127 | #*.log
128 | local_settings.py
129 | db.sqlite3
130 | db.sqlite3-journal
131 |
132 | # Flask stuff:
133 | instance/
134 | .webassets-cache
135 |
136 | # Scrapy stuff:
137 | .scrapy
138 |
139 | # Sphinx documentation
140 | docs/_build/
141 |
142 | # PyBuilder
143 | .pybuilder/
144 | target/
145 |
146 | # Jupyter Notebook
147 | .ipynb_checkpoints
148 |
149 | # IPython
150 | profile_default/
151 | ipython_config.py
152 |
153 | # pyenv
154 | # For a library or package, you might want to ignore these files since the code is
155 | # intended to run in multiple environments; otherwise, check them in:
156 | # .python-version
157 |
158 | # pipenv
159 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
160 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
161 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
162 | # install all needed dependencies.
163 | #Pipfile.lock
164 |
165 | # poetry
166 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
167 | # This is especially recommended for binary packages to ensure reproducibility, and is more
168 | # commonly ignored for libraries.
169 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
170 | #poetry.lock
171 |
172 | # pdm
173 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
174 | #pdm.lock
175 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
176 | # in version control.
177 | # https://pdm.fming.dev/#use-with-ide
178 | .pdm.toml
179 |
180 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
181 | __pypackages__/
182 |
183 | # Celery stuff
184 | celerybeat-schedule
185 | celerybeat.pid
186 |
187 | # SageMath parsed files
188 | *.sage.py
189 |
190 | # Environments
191 | .env
192 | .venv
193 | env/
194 | venv/
195 | ENV/
196 | env.bak/
197 | venv.bak/
198 |
199 | # Spyder project settings
200 | .spyderproject
201 | .spyproject
202 |
203 | # Rope project settings
204 | .ropeproject
205 |
206 | # mkdocs documentation
207 | /site
208 |
209 | # mypy
210 | .mypy_cache/
211 | .dmypy.json
212 | dmypy.json
213 |
214 | # Pyre type checker
215 | .pyre/
216 |
217 | # pytype static type analyzer
218 | .pytype/
219 |
220 | # Cython debug symbols
221 | cython_debug/
222 |
223 | # PyCharm
224 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
225 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
226 | # and can be added to the global gitignore or merged into this file. For a more nuclear
227 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
228 | #.idea/
229 |
230 | ### Python Patch ###
231 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
232 | poetry.toml
233 |
234 | # ruff
235 | .ruff_cache/
236 |
237 | # LSP config files
238 | pyrightconfig.json
239 |
240 | #main.py
241 | src/flutter/flet_onesignal/analysis_options.yaml
242 | src/flutter/flet_onesignal/.fvmrc
243 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 |
4 | Flet OneSignal
5 |
6 | ## 📖 Overview
7 |
8 | Flet OneSignal is an extension for Flet in Python, integrating the OneSignal package from Dart/Flutter. It enables push notifications and messaging for mobile apps, making it easier to connect your iOS and Android applications with OneSignal.
9 |
10 | ## ☕ Buy Me a Coffee
11 | If you liked this project, please consider supporting its development with a donation. Your contribution will help me maintain and improve it.
12 |
13 |
14 |
15 |
16 |
17 | ## 📦 Installation
18 | ##### You can install `flet-onesignal` using one of the following package managers:
19 |
20 | **POETRY**
21 |
22 | ```console
23 | $ poetry add flet-onesignal
24 | ```
25 |
26 | **PIP**
27 |
28 | ```console
29 | $ pip install flet-onesignal
30 | ```
31 |
32 | **UV**
33 |
34 | ```console
35 | $ uv add flet-onesignal
36 | ```
37 |
38 | ---
39 |
40 | ## 🛠️ Configuration in the pyproject.toml file.
41 |
42 | [More in ](https://flet.dev/blog/pyproject-toml-support-for-flet-build-command/) Support for flet build command.
43 |
44 | ```toml
45 | [project]
46 | name = "flet-onesignal-example"
47 | version = "0.1.0"
48 | description = "flet-onesignal-example"
49 | readme = "README.md"
50 | requires-python = ">=3.12"
51 | authors = [
52 | { name = "developer", email = "you@example.com" }
53 | ]
54 |
55 | dependencies = [
56 | "flet>=0.26.0",
57 | "flet-onesignal>=0.3.0",
58 | ]
59 |
60 | [tool.uv]
61 | dev-dependencies = [
62 | "flet[all]>=0.26.0",
63 | ]
64 |
65 | ```
66 |
67 | ## 🔥 Example
68 |
69 | ```Python
70 | import flet as ft
71 | import flet_onesignal as fos
72 | from functools import partial
73 |
74 | ONESIGNAL_APP_ID = "example-123a-12a3-1a23-abcd1ef23g45"
75 |
76 |
77 | async def main(page: ft.Page):
78 | page.appbar = ft.AppBar(title=ft.Text("OneSignal Test"), bgcolor=ft.Colors.BLUE_700, color=ft.Colors.WHITE)
79 | get_onesignal_id = ft.TextField(label='Get OneSignal ID', read_only=True)
80 | get_external_id = ft.TextField(label='Get External User ID', read_only=True, ignore_pointers=True)
81 | set_external_id = ft.TextField(label='Set External User ID', hint_text='User ID')
82 | language = ft.TextField(label='Language', hint_text='Language Code (en)', value='en', color=ft.Colors.GREEN)
83 |
84 | def handle_notification_opened(e):
85 | #Access the data of the clicked notification
86 | list_view.content.controls.append(ft.Text(f"Notification opened: {e.notification_opened}"))
87 | list_view.update()
88 |
89 | def handle_notification_received(e):
90 | # Access the data of the received notification
91 | list_view.content.controls.append(ft.Text(f"Notification received: {e.notification_received}"))
92 | list_view.update()
93 |
94 | def handle_click_in_app_messages(e):
95 | # Access the data of the received notification in app messages
96 | list_view.content.controls.append(ft.Text(f"Notification click_in_app_messages: {e.click_in_app_messages}"))
97 | list_view.update()
98 |
99 | def get_id(e):
100 | result = onesignal.get_onesignal_id()
101 | get_onesignal_id.value = result
102 | get_onesignal_id.update()
103 |
104 | def get_external_user_id(e):
105 | result = onesignal.get_external_user_id()
106 | get_external_id.value = result
107 | get_external_id.update()
108 |
109 | def handle_login(e, external_user_id):
110 | message = "Login failed"
111 |
112 | if not external_user_id.value:
113 | message = "Please enter external user ID"
114 |
115 | if external_user_id.value:
116 | result = onesignal.login(external_user_id.value)
117 | if result:
118 | message = "Login successful"
119 |
120 | list_view.content.controls.append(ft.Text(message))
121 | list_view.update()
122 |
123 | def handle_logout(e):
124 | onesignal.logout()
125 | set_external_id.value = None
126 | set_external_id.update()
127 |
128 | def set_language(e, language_code):
129 | result = onesignal.set_language(language_code.value)
130 | list_view.content.controls.append(ft.Text(result))
131 | list_view.update()
132 | print(result)
133 |
134 | def handle_error(e):
135 | #handle_error
136 | list_view.content.controls.append(ft.Text(f"Error: {e.data}"))
137 | list_view.update()
138 |
139 | onesignal = fos.OneSignal(
140 | settings=fos.OneSignalSettings(app_id=ONESIGNAL_APP_ID),
141 | on_notification_opened=handle_notification_opened,
142 | on_notification_received=handle_notification_received,
143 | on_click_in_app_messages=handle_click_in_app_messages,
144 | on_error=handle_error,
145 | )
146 |
147 | container = ft.Container(
148 | alignment=ft.alignment.bottom_center,
149 | content=ft.Row(
150 | scroll=ft.ScrollMode.ADAPTIVE,
151 | expand=True,
152 | vertical_alignment=ft.CrossAxisAlignment.CENTER,
153 | controls=[
154 | ft.ElevatedButton(
155 | text='Get OneSignal Id',
156 | on_click=get_id
157 | ),
158 | ft.ElevatedButton(
159 | 'Get External User Id',
160 | on_click=get_external_user_id
161 | ),
162 | ft.ElevatedButton(
163 | text='Set External User Id',
164 | on_click=partial(handle_login, external_user_id=set_external_id)
165 | ),
166 | ft.ElevatedButton(
167 | text='Logout External User Id',
168 | on_click=handle_logout
169 | ),
170 | ft.ElevatedButton(
171 | text='Set Language',
172 | on_click=partial(set_language, language_code=language)
173 | ),
174 | ]
175 | )
176 | )
177 |
178 | list_view = ft.Container(
179 | expand=True,
180 | content=ft.ListView(
181 | padding=ft.padding.all(10),
182 | spacing=5,
183 | )
184 | )
185 | page.overlay.append(onesignal)
186 |
187 | page.add(
188 | # onesignal,
189 | list_view,
190 | get_onesignal_id,
191 | get_external_id,
192 | set_external_id,
193 | language,
194 | container,
195 | )
196 |
197 |
198 | if __name__ == "__main__":
199 | ft.app(target=main)
200 |
201 | ```
202 | ## 🤝🏽 Contributing
203 | Contributions and feedback are welcome!
204 |
205 | #### To contribute:
206 |
207 | 1. **Fork the repository.**
208 | 2. **Create a feature branch.**
209 | 3. **Submit a pull request with a detailed explanation of your changes.**
210 |
211 | ---
212 |
213 | ## 🚀 Try **flet-onesignal** today and enhance your Flet apps with push notifications!🔔
214 |
215 |
216 |
217 | [Commit your work to the LORD, and your plans will succeed. Proverbs 16: 3](https://www.bible.com/bible/116/PRO.16.NLT)
218 |
--------------------------------------------------------------------------------
/src/flutter/flet_onesignal/lib/src/flet_onesignal.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'package:flet/flet.dart';
3 | import 'package:flet_onesignal/src/utils/funcs.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:onesignal_flutter/onesignal_flutter.dart';
6 |
7 | class FletOneSignalControl extends StatefulWidget {
8 | final Control? parent;
9 | final Control control;
10 | final FletControlBackend backend;
11 |
12 | const FletOneSignalControl({
13 | super.key,
14 | required this.parent,
15 | required this.control,
16 | required this.backend,
17 | });
18 |
19 | @override
20 | State createState() => _FletOneSignalControlState();
21 | }
22 |
23 | class _FletOneSignalControlState extends State
24 | with FletStoreMixin {
25 | @override
26 | void initState() {
27 | super.initState();
28 | _initializeOneSignal();
29 | }
30 |
31 | @override
32 | void didChangeDependencies() {
33 | super.didChangeDependencies();
34 | _setupNotificationHandler();
35 | }
36 |
37 | void _initializeOneSignal() {
38 | final settings = widget.control.attrString("settings", "{}")!;
39 |
40 | Map settingsData;
41 |
42 | if (settings != "{}") {
43 | try {
44 | settingsData = jsonDecode(settings);
45 | final appId = settingsData["app_id"] ?? "DEFAULT_APP_ID";
46 | OneSignal.initialize(appId);
47 | } catch (error, stackTrace) {
48 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
49 | handleFlutterError(
50 | method: "_initializeOneSignal",
51 | error: error,
52 | stackTrace: stackTrace,
53 | trigger: widget.backend.triggerControlEvent,
54 | controlId: widget.control.id,
55 | );
56 | }
57 | }
58 | }
59 |
60 | void _setupNotificationHandler() {
61 | OneSignal.Notifications.addClickListener((event) {
62 | try {
63 | debugPrint("Notifications-addClickListener");
64 |
65 | final jsonData = jsonEncode({
66 | "json_data": event.notification.jsonRepresentation(),
67 | });
68 |
69 | widget.backend.triggerControlEvent(
70 | widget.control.id,
71 | "notification_received",
72 | jsonData,
73 | );
74 | } catch (error, stackTrace) {
75 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
76 | handleFlutterError(
77 | method: "_setupNotificationHandler",
78 | error: error,
79 | stackTrace: stackTrace,
80 | trigger: widget.backend.triggerControlEvent,
81 | controlId: widget.control.id,
82 | );
83 | }
84 | });
85 |
86 | OneSignal.Notifications.addForegroundWillDisplayListener((event) {
87 | try {
88 | debugPrint("Notifications-addForegroundWillDisplayListener");
89 |
90 | final jsonData = jsonEncode({
91 | "json_data": event.notification.jsonRepresentation(),
92 | });
93 |
94 | widget.backend.triggerControlEvent(widget.control.id, "notification_opened", jsonData);
95 |
96 | } catch (error, stackTrace) {
97 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
98 | handleFlutterError(
99 | method: "_setupNotificationHandler",
100 | error: error,
101 | stackTrace: stackTrace,
102 | trigger: widget.backend.triggerControlEvent,
103 | controlId: widget.control.id,
104 | );
105 | }
106 | });
107 |
108 | OneSignal.InAppMessages.addClickListener((event) async {
109 | try {
110 | debugPrint("InAppMessages-addClickListener");
111 |
112 | var messageMap = jsonDecode(event.message.jsonRepresentation());
113 | var resultMap = jsonDecode(event.result.jsonRepresentation());
114 |
115 | final jsonData = jsonEncode({
116 | "json_data": {
117 | "message": messageMap,
118 | "result": resultMap,
119 | }
120 | });
121 |
122 | widget.backend.triggerControlEvent(widget.control.id, "click_in_app_messages", jsonData);
123 |
124 | } catch (error, stackTrace) {
125 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
126 | handleFlutterError(
127 | method: "_setupNotificationHandler",
128 | error: error,
129 | stackTrace: stackTrace,
130 | trigger: widget.backend.triggerControlEvent,
131 | controlId: widget.control.id,
132 | );
133 | }
134 | });
135 |
136 | OneSignal.InAppMessages.addWillDisplayListener((event) async {
137 | try {
138 | debugPrint("InAppMessages-addWillDisplayListener");
139 |
140 | var messageMap = jsonDecode(event.message.jsonRepresentation());
141 |
142 | final jsonData = jsonEncode({
143 | "json_data": messageMap,
144 | });
145 |
146 | debugPrint("dataStr: $jsonData");
147 |
148 | widget.backend.triggerControlEvent(
149 | widget.control.id, "will_display_in_app_messages", jsonData);
150 | } catch (error, stackTrace) {
151 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
152 | handleFlutterError(
153 | method: "_setupNotificationHandler",
154 | error: error,
155 | stackTrace: stackTrace,
156 | trigger: widget.backend.triggerControlEvent,
157 | controlId: widget.control.id,
158 | );
159 | }
160 | });
161 |
162 | OneSignal.InAppMessages.addDidDisplayListener((event) async {
163 | try {
164 | debugPrint("InAppMessages-addDidDisplayListener");
165 |
166 | var messageMap = jsonDecode(event.message.jsonRepresentation());
167 |
168 | final jsonData = jsonEncode({
169 | "json_data": messageMap,
170 | });
171 |
172 | debugPrint("dataStr: $jsonData");
173 |
174 | widget.backend.triggerControlEvent(
175 | widget.control.id, "did_display_in_app_messages", jsonData);
176 | } catch (error, stackTrace) {
177 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
178 | handleFlutterError(
179 | method: "_setupNotificationHandler",
180 | error: error,
181 | stackTrace: stackTrace,
182 | trigger: widget.backend.triggerControlEvent,
183 | controlId: widget.control.id,
184 | );
185 | }
186 | });
187 |
188 | OneSignal.InAppMessages.addWillDismissListener((event) async {
189 | try {
190 | debugPrint("InAppMessages-addWillDismissListener");
191 |
192 | var messageMap = jsonDecode(event.message.jsonRepresentation());
193 |
194 | final jsonData = jsonEncode({
195 | "json_data": messageMap,
196 | });
197 |
198 | debugPrint("dataStr: $jsonData");
199 |
200 | widget.backend.triggerControlEvent(
201 | widget.control.id, "will_dismiss_in_app_messages", jsonData);
202 | } catch (error, stackTrace) {
203 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
204 | handleFlutterError(
205 | method: "_setupNotificationHandler",
206 | error: error,
207 | stackTrace: stackTrace,
208 | trigger: widget.backend.triggerControlEvent,
209 | controlId: widget.control.id,
210 | );
211 | }
212 | });
213 |
214 | OneSignal.InAppMessages.addDidDismissListener((event) async {
215 | try {
216 | debugPrint("InAppMessages-addDidDismissListener");
217 |
218 | var messageMap = jsonDecode(event.message.jsonRepresentation());
219 |
220 | final jsonData = jsonEncode({
221 | "json_data": messageMap,
222 | });
223 |
224 | debugPrint("dataStr: $jsonData");
225 |
226 | widget.backend.triggerControlEvent(
227 | widget.control.id, "did_dismiss_in_app_messages", jsonData);
228 | } catch (error, stackTrace) {
229 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
230 | handleFlutterError(
231 | method: "_setupNotificationHandler",
232 | error: error,
233 | stackTrace: stackTrace,
234 | trigger: widget.backend.triggerControlEvent,
235 | controlId: widget.control.id,
236 | );
237 | }
238 | });
239 | }
240 |
241 | @override
242 | Widget build(BuildContext context) {
243 | debugPrint("OneSignal build: ${widget.control.id} (${widget.control.hashCode})");
244 |
245 | () async {
246 | try {
247 | widget.backend.subscribeMethods(widget.control.id,
248 | (methodName, args) async {
249 | try {
250 | return switch (methodName) {
251 | "get_onesignal_id" => _getOnesignalId(),
252 | "get_external_user_id" => _getExternalUserId(),
253 | "login" => _login(args),
254 | "logout" => _logout(),
255 | "add_alias" => _addAlias(args),
256 | "remove_alias" => _removeAlias(args),
257 | "set_language" => _setLanguage(args),
258 | "consent_required" => _consentRequired(args),
259 | "request_permission" => _requestPermission(args),
260 | "opt_in" => _optIn(),
261 | "opt_out" => _optOut(),
262 | "register_for_provisional_authorization" => _registerForProvisionalAuthorization(),
263 | "can_request_permission" => _canRequestPermission(),
264 | "remove_notification" => _removeNotification(args),
265 | "remove_grouped_notifications" => _removeGroupedNotifications(args),
266 | "clear_all_notifications" => _clearAllNotifications(),
267 | "prevent_default" => _preventDefault(args),
268 | _ => null,
269 | };
270 | } catch (error, stackTrace) {
271 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
272 | handleFlutterError(
273 | method: methodName,
274 | error: error,
275 | stackTrace: stackTrace,
276 | trigger: widget.backend.triggerControlEvent,
277 | controlId: widget.control.id,
278 | );
279 | return error.toString();
280 | }
281 | });
282 | } catch (error, stackTrace) {
283 | debugPrint("Error:\nerror: $error\nstackTrace: $stackTrace");
284 | handleFlutterError(
285 | method: "subscribeMethods",
286 | error: error,
287 | stackTrace: stackTrace,
288 | trigger: widget.backend.triggerControlEvent,
289 | controlId: widget.control.id,
290 | );
291 | }
292 | }();
293 |
294 | return const SizedBox.shrink();
295 | }
296 |
297 | Future _getOnesignalId() async {
298 | var result = await OneSignal.User.getOnesignalId() ?? "The OneSignal ID does not exist.";
299 | return result;
300 | }
301 |
302 | Future _getExternalUserId() async {
303 | var result = await OneSignal.User.getExternalId() ?? "The external user ID does not yet exist.";
304 | return result;
305 | }
306 |
307 | Future _login(Map args) async {
308 | var externalUserId = args["external_user_id"] ?? "";
309 | await OneSignal.login(externalUserId);
310 | return null;
311 | }
312 |
313 | Future _logout() async {
314 | await OneSignal.logout();
315 | return null;
316 | }
317 |
318 | Future _addAlias(Map args) async {
319 | String alias = args["alias"] ?? "";
320 | dynamic idAlias = args["id_alias"];
321 | await OneSignal.User.addAlias(alias, idAlias);
322 | return null;
323 | }
324 |
325 | Future _removeAlias(Map args) async {
326 | String alias = args["alias"] ?? "";
327 | await OneSignal.User.removeAlias(alias);
328 | return null;
329 | }
330 |
331 | Future _setLanguage(Map args) async {
332 | String language = args["language"] ?? "en";
333 | await OneSignal.User.setLanguage(language);
334 | return null;
335 | }
336 |
337 | Future _consentRequired(Map args) async {
338 | String dataStr = args["data"]!;
339 | Map dataMap = json.decode(dataStr);
340 | bool consent = dataMap["consent"] as bool;
341 | await OneSignal.consentRequired(consent);
342 | return null;
343 | }
344 |
345 | Future _requestPermission(Map args) async {
346 | String dataStr = args["data"]!;
347 | Map dataMap = json.decode(dataStr);
348 | bool fallbackToSettings = dataMap["fallback_to_settings"] as bool;
349 | var result = await OneSignal.Notifications.requestPermission(fallbackToSettings);
350 | return result.toString();
351 | }
352 |
353 | Future _optIn() async {
354 | await OneSignal.User.pushSubscription.optIn();
355 | return null;
356 | }
357 |
358 | Future _optOut() async {
359 | await OneSignal.User.pushSubscription.optOut();
360 | return null;
361 | }
362 |
363 | Future _registerForProvisionalAuthorization() async {
364 | var result = await OneSignal.Notifications.registerForProvisionalAuthorization(true);
365 | return result.toString();
366 | }
367 |
368 | Future _canRequestPermission() async {
369 | var result = await OneSignal.Notifications.canRequest();
370 | return result.toString();
371 | }
372 |
373 | Future _removeNotification(Map args) async {
374 | int notificationId = args["notification_id"] as int;
375 | await OneSignal.Notifications.removeNotification(notificationId);
376 | return null;
377 | }
378 |
379 | Future _removeGroupedNotifications(Map args) async {
380 | var notificationGroup = args["notification_group"] ?? "";
381 | await OneSignal.Notifications.removeGroupedNotifications(
382 | notificationGroup);
383 | return null;
384 | }
385 |
386 | Future _clearAllNotifications() async {
387 | await OneSignal.Notifications.clearAll();
388 | return null;
389 | }
390 |
391 | Future _preventDefault(Map args) async {
392 | var notificationId = args["notification_id"] ?? "";
393 | OneSignal.Notifications.preventDefault(notificationId);
394 | return null;
395 | }
396 | }
397 |
--------------------------------------------------------------------------------
/src/flet_onesignal/flet_onesignal.py:
--------------------------------------------------------------------------------
1 | import json
2 | from flet.core.ref import Ref
3 | from typing import Any, Optional
4 | from dataclasses import dataclass
5 | from flet.core.control import Control
6 | from flet_onesignal.languages import Language
7 | from flet.core.event_handler import EventHandler
8 | from flet.core.control_event import ControlEvent
9 | from flet.core.types import OptionalEventCallable, OptionalControlEventCallable
10 |
11 |
12 | @dataclass
13 | class OneSignalSettings:
14 | app_id: str
15 |
16 |
17 | @dataclass
18 | class OneSignalChangeEvent(ControlEvent):
19 | def __init__(self, e: ControlEvent):
20 | super().__init__(e.target, e.name, e.data, e.control, e.page)
21 | data = json.loads(e.data)
22 | self.notification_received: dict = data.get("json_data")
23 | self.notification_opened: dict = data.get("json_data")
24 | self.click_in_app_messages : dict = data.get("json_data")
25 | self.will_display_in_app_messages: dict = data.get("json_data")
26 | self.did_display_in_app_messages: dict = data.get("json_data")
27 | self.will_dismiss_in_app_messages: dict = data.get("json_data")
28 | self.did_dismiss_in_app_messages: dict = data.get("json_data")
29 |
30 |
31 | class OneSignal(Control):
32 | """
33 | A control that allows you to send push notifications and messages to mobile applications. This control makes it easy
34 | to integrate your iOS and/or Android applications with OneSignal.
35 |
36 | This control is not visual and should be added to the `page.overlay` list.
37 | """
38 |
39 | def __init__(
40 | self,
41 | # Control
42 | #
43 | ref: Optional[Ref] = None,
44 | data: Any = None,
45 | settings: OneSignalSettings = None,
46 | on_notification_opened: OptionalEventCallable[OneSignalChangeEvent] = None,
47 | on_notification_received: OptionalEventCallable[OneSignalChangeEvent] = None,
48 | on_click_in_app_messages: OptionalEventCallable[OneSignalChangeEvent] = None,
49 | on_will_display_in_app_messages: OptionalEventCallable[OneSignalChangeEvent] = None,
50 | on_did_display_in_app_messages: OptionalEventCallable[OneSignalChangeEvent] = None,
51 | on_will_dismiss_in_app_messages: OptionalEventCallable[OneSignalChangeEvent] = None,
52 | on_did_dismiss_in_app_messages: OptionalEventCallable[OneSignalChangeEvent] = None,
53 | on_error: OptionalControlEventCallable = None,
54 | ):
55 | Control.__init__(
56 | self,
57 | ref=ref,
58 | data=data,
59 | )
60 | # handlers attributes
61 | self.__on_notification_opened = EventHandler(result_converter=lambda e: OneSignalChangeEvent(e))
62 | self._add_event_handler(event_name="notification_opened", handler=self.__on_notification_opened.get_handler())
63 | self.__on_notification_received = EventHandler(result_converter=lambda e: OneSignalChangeEvent(e))
64 | self._add_event_handler(event_name="notification_received", handler=self.__on_notification_received.get_handler())
65 | self.__on_click_in_app_messages = EventHandler(result_converter=lambda e: OneSignalChangeEvent(e))
66 | self._add_event_handler(event_name="click_in_app_messages", handler=self.__on_click_in_app_messages.get_handler())
67 | self.__on_will_display_in_app_messages = EventHandler(result_converter=lambda e: OneSignalChangeEvent(e))
68 | self._add_event_handler(event_name="will_display_in_app_messages", handler=self.__on_will_display_in_app_messages.get_handler())
69 | self.__on_did_display_in_app_messages = EventHandler(result_converter=lambda e: OneSignalChangeEvent(e))
70 | self._add_event_handler(event_name="did_display_in_app_messages", handler=self.__on_did_display_in_app_messages.get_handler())
71 | self.__on_will_dismiss_in_app_messages = EventHandler(result_converter=lambda e: OneSignalChangeEvent(e))
72 | self._add_event_handler(event_name="will_dismiss_in_app_messages", handler=self.__on_will_dismiss_in_app_messages.get_handler())
73 | self.__on_did_dismiss_in_app_messages = EventHandler(result_converter=lambda e: OneSignalChangeEvent(e))
74 | self._add_event_handler(event_name="did_dismiss_in_app_messages", handler=self.__on_did_dismiss_in_app_messages.get_handler())
75 | #
76 | #
77 | self.on_notification_opened = on_notification_opened
78 | self.on_notification_received = on_notification_received
79 | self.on_click_in_app_messages = on_click_in_app_messages
80 | self.on_will_display_in_app_messages = on_will_display_in_app_messages
81 | self.on_did_display_in_app_messages = on_did_display_in_app_messages
82 | self.on_will_dismiss_in_app_messages = on_will_dismiss_in_app_messages
83 | self.on_did_dismiss_in_app_messages = on_did_dismiss_in_app_messages
84 | self.on_error = on_error
85 | self.settings = settings
86 |
87 | def _get_control_name(self):
88 | return "flet_onesignal"
89 |
90 | def before_update(self):
91 | if self.settings is not None:
92 | self._set_attr_json("settings", self.settings)
93 |
94 | def get_onesignal_id(self, wait_timeout: Optional[float] = 25) -> str:
95 | """Returns the OneSignal ID for the current user, which may be None."""
96 |
97 | result = self.invoke_method(
98 | method_name="get_onesignal_id",
99 | wait_for_result=True,
100 | wait_timeout=wait_timeout
101 | )
102 |
103 | return result
104 |
105 | async def get_onesignal_id_async(self, wait_timeout: Optional[float] = 25) -> str:
106 | """Returns the OneSignal ID for the current user, which may be None."""
107 |
108 | result = await self.invoke_method_async(
109 | method_name="get_onesignal_id",
110 | wait_for_result=True,
111 | wait_timeout=wait_timeout
112 | )
113 |
114 | return result
115 |
116 | def get_external_user_id(self, wait_timeout: Optional[float] = 25):
117 | """Returns the External ID for the current user, which may be None."""
118 |
119 | result = self.invoke_method(
120 | method_name="get_external_user_id",
121 | wait_for_result=True,
122 | wait_timeout=wait_timeout
123 | )
124 |
125 | return result
126 |
127 | async def get_external_user_id_async(self, wait_timeout: Optional[float] = 25):
128 | """Returns the External ID for the current user, which may be None."""
129 |
130 | result = await self.invoke_method_async(
131 | method_name="get_external_user_id",
132 | wait_for_result=True,
133 | wait_timeout=wait_timeout
134 | )
135 |
136 | return result
137 |
138 | def login(self, external_user_id: str) -> bool:
139 | """Login to OneSignal under the user identified by the [external_user_id] provided. The act of logging a user into
140 | the OneSignal SDK will switch the user context to that specific user."""
141 |
142 | self.invoke_method(
143 | method_name="login",
144 | arguments={"external_user_id": external_user_id},
145 | wait_for_result=True,
146 | )
147 |
148 | if self.get_external_user_id():
149 | return True
150 |
151 | return False
152 |
153 | async def login_async(self, external_user_id: str) -> bool:
154 | """Login to OneSignal under the user identified by the [external_user_id] provided. The act of logging a user into
155 | the OneSignal SDK will switch the user context to that specific user."""
156 |
157 | await self.invoke_method_async(
158 | method_name="login",
159 | arguments={"external_user_id": external_user_id},
160 | wait_for_result=True,
161 | )
162 |
163 | if await self.get_external_user_id_async():
164 | return True
165 |
166 | return False
167 |
168 |
169 | def logout(self) -> None:
170 | """Logout the user previously logged in via [login]. The user property now references a new device-scoped user.
171 | A device-scoped user has no user identity that can later be retrieved, except through this device as long as the
172 | app remains installed and the app data is not cleared."""
173 |
174 | self.invoke_method(
175 | method_name="logout",
176 | wait_for_result=True,
177 | )
178 |
179 | async def logout_async(self) -> None:
180 | """Logout the user previously logged in via [login]. The user property now references a new device-scoped user.
181 | A device-scoped user has no user identity that can later be retrieved, except through this device as long as the
182 | app remains installed and the app data is not cleared."""
183 |
184 | await self.invoke_method_async(
185 | method_name="logout",
186 | wait_for_result=True,
187 | )
188 |
189 | def add_alias(self, alias: str, id_alias: any) -> None:
190 | """Set an [alias] for the current user. If this [alias] label already exists on this user, it will be
191 | overwritten with the new alias [id]."""
192 |
193 | args = {
194 | 'alias': alias,
195 | 'id_alias': id_alias,
196 | }
197 |
198 | self.invoke_method(
199 | method_name="add_alias",
200 | arguments=args,
201 | wait_for_result=True,
202 | )
203 |
204 | async def add_alias_async(self, alias: str, id_alias: any) -> None:
205 | """Set an [alias] for the current user. If this [alias] label already exists on this user, it will be
206 | overwritten with the new alias [id]."""
207 |
208 | args = {
209 | 'alias': alias,
210 | 'id_alias': id_alias,
211 | }
212 |
213 | await self.invoke_method_async(
214 | method_name="add_alias",
215 | arguments=args,
216 | wait_for_result=True,
217 | )
218 |
219 | def remove_alias(self, alias: str) -> None:
220 | """Remove an [alias] from the current user."""
221 |
222 | self.invoke_method(
223 | method_name="remove_alias",
224 | arguments={'alias': alias},
225 | wait_for_result=True,
226 | )
227 |
228 | async def remove_alias_async(self, alias: str) -> None:
229 | """Remove an [alias] from the current user."""
230 |
231 | await self.invoke_method_async(
232 | method_name="remove_alias",
233 | arguments={'alias': alias},
234 | wait_for_result=True,
235 | )
236 |
237 | def set_language(self, language_code: str = 'en') -> str:
238 | """Sets the user's language.
239 | Sets the user's language to [language] this also applies to the email and/or SMS player if those are logged in
240 | on the device."""
241 |
242 | if language_code in Language._value2member_map_:
243 | self.invoke_method(
244 | method_name="set_language",
245 | arguments={'language': language_code},
246 | wait_for_result=True,
247 | )
248 | return 'Language set successfully.'
249 |
250 | return 'Language not found.'
251 |
252 | async def set_language_async(self, language_code: str = 'en') -> str:
253 | """Sets the user's language.
254 | Sets the user's language to [language] this also applies to the email and/or SMS player if those are logged in
255 | on the device."""
256 |
257 | if language_code in Language._value2member_map_:
258 | await self.invoke_method_async(
259 | method_name="set_language",
260 | arguments={'language': language_code},
261 | wait_for_result=True,
262 | )
263 | return 'Language set successfully.'
264 |
265 | return 'Language not found.'
266 |
267 | def remove_notification(self, notification_id: int) -> None:
268 | """Removes a single notification on Android devices."""
269 |
270 | platform = self.page.platform.value
271 |
272 | if platform == 'android':
273 | self.invoke_method(
274 | method_name="remove_notification",
275 | arguments={"notification_id": str(notification_id)},
276 | wait_for_result=True,
277 | )
278 |
279 | async def remove_notification_async(self, notification_id: int) -> None:
280 | """Removes a single notification on Android devices."""
281 |
282 | platform = self.page.platform.value
283 |
284 | if platform == 'android':
285 | await self.invoke_method_async(
286 | method_name="remove_notification",
287 | arguments={"notification_id": str(notification_id)},
288 | wait_for_result=True,
289 | )
290 |
291 | def remove_grouped_notifications(self, notification_group: str) -> None:
292 | """Removes a grouped notification on Android devices."""
293 |
294 | platform = self.page.platform.value
295 |
296 | if platform == 'android':
297 | self.invoke_method(
298 | method_name="remove_grouped_notifications",
299 | arguments={"notification_group": notification_group},
300 | wait_for_result=True,
301 | )
302 |
303 | async def remove_grouped_notifications_async(self, notification_group: str) -> None:
304 | """Removes a grouped notification on Android devices."""
305 |
306 | platform = self.page.platform.value
307 |
308 | if platform == 'android':
309 | await self.invoke_method_async(
310 | method_name="remove_grouped_notifications",
311 | arguments={"notification_group": notification_group},
312 | wait_for_result=True,
313 | )
314 |
315 | def clear_all_notifications(self) -> None:
316 | """Removes all OneSignal notifications."""
317 |
318 | self.invoke_method(
319 | method_name="clear_all_notifications",
320 | wait_for_result=True,
321 | )
322 |
323 | async def clear_all_notifications_async(self) -> None:
324 | """Removes all OneSignal notifications."""
325 |
326 | await self.invoke_method_async(
327 | method_name="clear_all_notifications",
328 | wait_for_result=True,
329 | )
330 |
331 | def consent_required(self, consent: bool = True) -> None:
332 | """Allows you to completely disable the SDK until your app calls the OneSignal.consentGiven(true) function.
333 | This is useful if you want to show a Terms and Conditions or privacy popup for GDPR."""
334 |
335 | data_str = json.dumps({"consent": consent})
336 |
337 | self.invoke_method(
338 | method_name="consent_required",
339 | arguments={'data': data_str},
340 | wait_for_result=True,
341 | )
342 |
343 | async def consent_required_async(self, consent: bool = True) -> None:
344 | """Allows you to completely disable the SDK until your app calls the OneSignal.consentGiven(true) function.
345 | This is useful if you want to show a Terms and Conditions or privacy popup for GDPR."""
346 |
347 | data_str = json.dumps({"consent": consent})
348 |
349 | await self.invoke_method_async(
350 | method_name="consent_required",
351 | arguments={'data': data_str},
352 | wait_for_result=True,
353 | )
354 |
355 | def request_permission(self, fallback_to_settings: bool = True, wait_timeout: Optional[float] = 25) -> bool:
356 | """Prompt the user for permission to receive push notifications. This will display the native system prompt to
357 | request push notification permission."""
358 |
359 | data_str = json.dumps({"fallback_to_settings": fallback_to_settings})
360 |
361 | result = self.invoke_method(
362 | method_name="request_permission",
363 | arguments={'data': data_str},
364 | wait_for_result=True,
365 | wait_timeout=wait_timeout
366 | )
367 |
368 | return result == "true"
369 |
370 | async def request_permission_async(self, fallback_to_settings: bool = True, wait_timeout: Optional[float] = 25) -> bool:
371 | """Prompt the user for permission to receive push notifications. This will display the native system prompt to
372 | request push notification permission."""
373 |
374 | data_str = json.dumps({"fallback_to_settings": fallback_to_settings})
375 |
376 | result = await self.invoke_method_async(
377 | method_name="request_permission",
378 | arguments={'data': data_str},
379 | wait_for_result=True,
380 | wait_timeout=wait_timeout
381 | )
382 |
383 | return result == "true"
384 |
385 | # Métodos para integração com o OneSignal Notifications
386 | def register_for_provisional_authorization(self, wait_timeout: Optional[float] = 25) -> bool:
387 | """Instead of having to prompt the user for permission to send them push notifications, your app can request
388 | provisional authorization."""
389 |
390 | result = self.invoke_method(
391 | method_name="register_for_provisional_authorization",
392 | wait_for_result=True,
393 | wait_timeout=wait_timeout
394 | )
395 | return result == "true"
396 |
397 | async def register_for_provisional_authorization_async(self, wait_timeout: Optional[float] = 25) -> bool:
398 | """Instead of having to prompt the user for permission to send them push notifications, your app can request
399 | provisional authorization."""
400 |
401 | result = await self.invoke_method_async(
402 | method_name="register_for_provisional_authorization",
403 | wait_for_result=True,
404 | wait_timeout=wait_timeout
405 | )
406 | return result == "true"
407 |
408 | def can_request_permission(self, wait_timeout: Optional[float] = 25) -> bool:
409 | """Whether attempting to request notification permission will show a prompt. Returns true if the device has not
410 | been prompted for push notification permission already."""
411 |
412 | result = self.invoke_method(
413 | method_name="can_request_permission",
414 | wait_for_result=True,
415 | wait_timeout=wait_timeout
416 | )
417 | return result == "true"
418 |
419 | async def can_request_permission_async(self, wait_timeout: Optional[float] = 25) -> bool:
420 | """Whether attempting to request notification permission will show a prompt. Returns true if the device has not
421 | been prompted for push notification permission already."""
422 |
423 | result = await self.invoke_method_async(
424 | method_name="can_request_permission",
425 | wait_for_result=True,
426 | wait_timeout=wait_timeout
427 | )
428 | return result == "true"
429 |
430 | def opt_in(self) -> None:
431 | """Call this method to receive push notifications on the device or to resume receiving of push notifications
432 | after calling optOut. If needed, this method will prompt the user for push notifications permission."""
433 |
434 | self.invoke_method(
435 | method_name="opt_in",
436 | wait_for_result=True,
437 | )
438 |
439 | async def opt_in_async(self) -> None:
440 | """Call this method to receive push notifications on the device or to resume receiving of push notifications
441 | after calling optOut. If needed, this method will prompt the user for push notifications permission."""
442 |
443 | await self.invoke_method_async(
444 | method_name="opt_in",
445 | wait_for_result=True,
446 | )
447 |
448 | def opt_out(self) -> None:
449 | """If at any point you want the user to stop receiving push notifications on the current device (regardless of
450 | system-level permission status), you can call this method to opt out."""
451 |
452 | self.invoke_method(
453 | method_name="opt_in",
454 | wait_for_result=True,
455 | )
456 |
457 | async def opt_out_async(self) -> None:
458 | """If at any point you want the user to stop receiving push notifications on the current device (regardless of
459 | system-level permission status), you can call this method to opt out."""
460 |
461 | await self.invoke_method_async(
462 | method_name="opt_in",
463 | wait_for_result=True,
464 | )
465 |
466 | def prevent_default(self, notification_id: str) -> None:
467 | """The notification willDisplay listener is called whenever a notification arrives and the application is in
468 | foreground"""
469 |
470 | self.invoke_method(
471 | method_name="prevent_default",
472 | arguments={"notification_id": notification_id},
473 | )
474 |
475 | @property
476 | def on_notification_opened(
477 | self,
478 | ) -> OptionalEventCallable[OneSignalChangeEvent]:
479 | return self.__on_notification_opened.handler
480 |
481 | @on_notification_opened.setter
482 | def on_notification_opened(
483 | self, handler: OptionalEventCallable[OneSignalChangeEvent]
484 | ):
485 | self.__on_notification_opened.handler = handler
486 | self._set_attr("notification_opened", True if handler is not None else None)
487 |
488 | @property
489 | def on_notification_received(
490 | self,
491 | ) -> OptionalEventCallable[OneSignalChangeEvent]:
492 | return self.__on_notification_received.handler
493 |
494 | @on_notification_received.setter
495 | def on_notification_received(
496 | self, handler: OptionalEventCallable[OneSignalChangeEvent]
497 | ):
498 | self.__on_notification_received.handler = handler
499 | self._set_attr("notification_received", True if handler is not None else None)
500 |
501 | @property
502 | def on_click_in_app_messages(
503 | self,
504 | ) -> OptionalEventCallable[OneSignalChangeEvent]:
505 | return self.__on_click_in_app_messages.handler
506 |
507 | @on_click_in_app_messages.setter
508 | def on_click_in_app_messages(
509 | self, handler: OptionalEventCallable[OneSignalChangeEvent]
510 | ):
511 | self.__on_click_in_app_messages.handler = handler
512 | self._set_attr("click_in_app_messages", True if handler is not None else None)
513 |
514 | @property
515 | def on_will_display_in_app_messages(
516 | self,
517 | ) -> OptionalEventCallable[OneSignalChangeEvent]:
518 | return self.__on_will_display_in_app_messages.handler
519 |
520 | @on_will_display_in_app_messages.setter
521 | def on_will_display_in_app_messages(
522 | self, handler: OptionalEventCallable[OneSignalChangeEvent]
523 | ):
524 | self.__on_will_display_in_app_messages.handler = handler
525 | self._set_attr("will_display_in_app_messages", True if handler is not None else None)
526 |
527 | @property
528 | def on_did_display_in_app_messages(
529 | self,
530 | ) -> OptionalEventCallable[OneSignalChangeEvent]:
531 | return self.__on_did_display_in_app_messages.handler
532 |
533 | @on_did_display_in_app_messages.setter
534 | def on_did_display_in_app_messages(
535 | self, handler: OptionalEventCallable[OneSignalChangeEvent]
536 | ):
537 | self.__on_did_display_in_app_messages.handler = handler
538 | self._set_attr("did_display_in_app_messages", True if handler is not None else None)
539 |
540 | @property
541 | def on_will_dismiss_in_app_messages(
542 | self,
543 | ) -> OptionalEventCallable[OneSignalChangeEvent]:
544 | return self.__on_will_dismiss_in_app_messages.handler
545 |
546 | @on_will_dismiss_in_app_messages.setter
547 | def on_will_dismiss_in_app_messages(
548 | self, handler: OptionalEventCallable[OneSignalChangeEvent]
549 | ):
550 | self.__on_will_dismiss_in_app_messages.handler = handler
551 | self._set_attr("will_dismiss_in_app_messages", True if handler is not None else None)
552 |
553 | @property
554 | def on_did_dismiss_in_app_messages(
555 | self,
556 | ) -> OptionalEventCallable[OneSignalChangeEvent]:
557 | return self.__on_did_dismiss_in_app_messages.handler
558 |
559 | @on_did_dismiss_in_app_messages.setter
560 | def on_did_dismiss_in_app_messages(
561 | self, handler: OptionalEventCallable[OneSignalChangeEvent]
562 | ):
563 | self.__on_did_dismiss_in_app_messages.handler = handler
564 | self._set_attr("did_dismiss_in_app_messages", True if handler is not None else None)
565 |
566 | @property
567 | def on_error(self) -> OptionalControlEventCallable:
568 | return self._get_attr("error")
569 |
570 | @on_error.setter
571 | def on_error(self, handler: OptionalControlEventCallable):
572 | self._add_event_handler("error", handler)
573 |
--------------------------------------------------------------------------------