flet 0.70.0.dev5776__py3-none-any.whl → 0.70.0.dev6145__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flet might be problematic. Click here for more details.
- flet/__init__.py +32 -4
- flet/components/__init__.py +0 -0
- flet/components/component.py +346 -0
- flet/components/component_decorator.py +24 -0
- flet/components/component_owned.py +22 -0
- flet/components/hooks/__init__.py +0 -0
- flet/components/hooks/hook.py +12 -0
- flet/components/hooks/use_callback.py +28 -0
- flet/components/hooks/use_context.py +91 -0
- flet/components/hooks/use_effect.py +104 -0
- flet/components/hooks/use_memo.py +52 -0
- flet/components/hooks/use_state.py +58 -0
- flet/components/memo.py +34 -0
- flet/components/observable.py +269 -0
- flet/components/public_utils.py +10 -0
- flet/components/utils.py +85 -0
- flet/controls/base_control.py +34 -10
- flet/controls/base_page.py +44 -40
- flet/controls/context.py +22 -1
- flet/controls/control_event.py +19 -2
- flet/controls/core/column.py +5 -0
- flet/controls/core/drag_target.py +17 -8
- flet/controls/core/row.py +5 -0
- flet/controls/core/view.py +6 -6
- flet/controls/cupertino/cupertino_icons.py +1 -1
- flet/controls/id_counter.py +24 -0
- flet/controls/material/divider.py +6 -0
- flet/controls/material/icons.py +1 -1
- flet/controls/material/textfield.py +10 -1
- flet/controls/material/vertical_divider.py +6 -0
- flet/controls/object_patch.py +434 -197
- flet/controls/page.py +203 -84
- flet/controls/services/haptic_feedback.py +0 -3
- flet/controls/services/shake_detector.py +0 -3
- flet/messaging/flet_socket_server.py +13 -6
- flet/messaging/session.py +103 -10
- flet/{controls/session_storage.py → messaging/session_store.py} +2 -2
- flet/version.py +1 -1
- {flet-0.70.0.dev5776.dist-info → flet-0.70.0.dev6145.dist-info}/METADATA +4 -5
- {flet-0.70.0.dev5776.dist-info → flet-0.70.0.dev6145.dist-info}/RECORD +43 -30
- flet/controls/cache.py +0 -87
- flet/controls/control_id.py +0 -22
- flet/controls/core/state_view.py +0 -60
- {flet-0.70.0.dev5776.dist-info → flet-0.70.0.dev6145.dist-info}/WHEEL +0 -0
- {flet-0.70.0.dev5776.dist-info → flet-0.70.0.dev6145.dist-info}/entry_points.txt +0 -0
- {flet-0.70.0.dev5776.dist-info → flet-0.70.0.dev6145.dist-info}/top_level.txt +0 -0
flet/controls/page.py
CHANGED
|
@@ -19,9 +19,11 @@ from urllib.parse import urlparse
|
|
|
19
19
|
|
|
20
20
|
from flet.auth.authorization import Authorization
|
|
21
21
|
from flet.auth.oauth_provider import OAuthProvider
|
|
22
|
+
from flet.components.component import Renderer
|
|
23
|
+
from flet.components.public_utils import unwrap_component
|
|
22
24
|
from flet.controls.base_control import BaseControl, control
|
|
23
25
|
from flet.controls.base_page import BasePage
|
|
24
|
-
from flet.controls.context import _context_page
|
|
26
|
+
from flet.controls.context import _context_page, context
|
|
25
27
|
from flet.controls.control import Control
|
|
26
28
|
from flet.controls.control_event import (
|
|
27
29
|
ControlEvent,
|
|
@@ -40,7 +42,6 @@ from flet.controls.services.service import Service
|
|
|
40
42
|
from flet.controls.services.shared_preferences import SharedPreferences
|
|
41
43
|
from flet.controls.services.storage_paths import StoragePaths
|
|
42
44
|
from flet.controls.services.url_launcher import UrlLauncher
|
|
43
|
-
from flet.controls.session_storage import SessionStorage
|
|
44
45
|
from flet.controls.types import (
|
|
45
46
|
AppLifecycleState,
|
|
46
47
|
Brightness,
|
|
@@ -50,6 +51,7 @@ from flet.controls.types import (
|
|
|
50
51
|
Wrapper,
|
|
51
52
|
)
|
|
52
53
|
from flet.utils import is_pyodide
|
|
54
|
+
from flet.utils.deprecated import deprecated
|
|
53
55
|
from flet.utils.strings import random_string
|
|
54
56
|
|
|
55
57
|
if not is_pyodide():
|
|
@@ -70,10 +72,6 @@ except ImportError:
|
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
logger = logging.getLogger("flet")
|
|
73
|
-
try:
|
|
74
|
-
from typing import ParamSpec
|
|
75
|
-
except ImportError:
|
|
76
|
-
from typing_extensions import ParamSpec
|
|
77
75
|
|
|
78
76
|
|
|
79
77
|
AT = TypeVar("AT", bound=Authorization)
|
|
@@ -175,18 +173,12 @@ class Page(BasePage):
|
|
|
175
173
|
|
|
176
174
|
sess: InitVar["Session"]
|
|
177
175
|
"""
|
|
178
|
-
|
|
176
|
+
The session that this page belongs to.
|
|
179
177
|
"""
|
|
180
178
|
|
|
181
179
|
multi_views: list[MultiView] = field(default_factory=list)
|
|
182
180
|
"""
|
|
183
|
-
|
|
184
|
-
"""
|
|
185
|
-
|
|
186
|
-
window: Window = field(default_factory=lambda: Window())
|
|
187
|
-
"""
|
|
188
|
-
Provides properties/methods/events to monitor and control the
|
|
189
|
-
app's native OS window.
|
|
181
|
+
The list of multi-views associated with this page.
|
|
190
182
|
"""
|
|
191
183
|
|
|
192
184
|
browser_context_menu: BrowserContextMenu = field(
|
|
@@ -204,82 +196,97 @@ class Page(BasePage):
|
|
|
204
196
|
default_factory=lambda: SharedPreferences(), metadata={"skip": True}
|
|
205
197
|
)
|
|
206
198
|
"""
|
|
207
|
-
|
|
199
|
+
Provides a persistent key-value storage for simple data types.
|
|
208
200
|
"""
|
|
209
201
|
|
|
210
202
|
clipboard: Clipboard = field(
|
|
211
203
|
default_factory=lambda: Clipboard(), metadata={"skip": True}
|
|
212
204
|
)
|
|
213
205
|
"""
|
|
214
|
-
|
|
206
|
+
Provides access to the system clipboard.
|
|
215
207
|
"""
|
|
216
208
|
|
|
217
209
|
storage_paths: StoragePaths = field(
|
|
218
210
|
default_factory=lambda: StoragePaths(), metadata={"skip": True}
|
|
219
211
|
)
|
|
220
212
|
"""
|
|
221
|
-
|
|
213
|
+
Provides the information about common storage paths.
|
|
222
214
|
"""
|
|
223
215
|
|
|
224
216
|
url_launcher: UrlLauncher = field(
|
|
225
217
|
default_factory=lambda: UrlLauncher(), metadata={"skip": True}
|
|
226
218
|
)
|
|
227
219
|
"""
|
|
228
|
-
|
|
220
|
+
Provides methods for launching URLs.
|
|
229
221
|
"""
|
|
230
222
|
|
|
231
|
-
|
|
223
|
+
window: Window = field(default_factory=lambda: Window())
|
|
232
224
|
"""
|
|
233
|
-
|
|
225
|
+
Provides properties/methods/events to monitor and control the
|
|
226
|
+
app's native OS window.
|
|
234
227
|
"""
|
|
235
228
|
|
|
236
|
-
|
|
237
|
-
"""
|
|
238
|
-
TBD
|
|
229
|
+
route: str = "/"
|
|
239
230
|
"""
|
|
231
|
+
Gets current app route.
|
|
240
232
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
Get or sets page's navigation route. See
|
|
244
|
-
[Navigation and routing](https://flet.dev/docs/getting-started/navigation-and-routing)
|
|
245
|
-
section for more information and examples.
|
|
233
|
+
Note:
|
|
234
|
+
This property is read-only.
|
|
246
235
|
"""
|
|
247
236
|
|
|
248
237
|
web: bool = False
|
|
249
238
|
"""
|
|
250
239
|
`True` if the application is running in the web browser.
|
|
240
|
+
|
|
241
|
+
Note:
|
|
242
|
+
This property is read-only.
|
|
251
243
|
"""
|
|
252
244
|
|
|
253
245
|
pwa: bool = False
|
|
254
246
|
"""
|
|
255
247
|
`True` if the application is running as Progressive Web App (PWA).
|
|
256
248
|
|
|
257
|
-
|
|
249
|
+
Note:
|
|
250
|
+
This property is read-only.
|
|
258
251
|
"""
|
|
259
|
-
|
|
260
252
|
debug: bool = False
|
|
261
253
|
"""
|
|
262
254
|
`True` if Flutter client of Flet app is running in debug mode.
|
|
255
|
+
|
|
256
|
+
Note:
|
|
257
|
+
This property is read-only.
|
|
263
258
|
"""
|
|
264
259
|
|
|
265
260
|
wasm: bool = False
|
|
266
261
|
"""
|
|
267
|
-
|
|
262
|
+
`True` if the application is running in WebAssembly (WASM) mode.
|
|
263
|
+
|
|
264
|
+
Note:
|
|
265
|
+
This property is read-only.
|
|
268
266
|
"""
|
|
269
267
|
|
|
270
268
|
test: bool = False
|
|
271
269
|
"""
|
|
272
|
-
|
|
270
|
+
`True` if the application is running with test mode.
|
|
271
|
+
|
|
272
|
+
Note:
|
|
273
|
+
This property is read-only.
|
|
273
274
|
"""
|
|
274
275
|
|
|
275
276
|
multi_view: bool = False
|
|
276
277
|
"""
|
|
277
|
-
|
|
278
|
+
`True` if the application is running with multi-view support.
|
|
279
|
+
|
|
280
|
+
Note:
|
|
281
|
+
This property is read-only.
|
|
278
282
|
"""
|
|
279
283
|
|
|
280
|
-
|
|
284
|
+
pyodide: bool = False
|
|
281
285
|
"""
|
|
282
|
-
|
|
286
|
+
`True` if the application is running in Pyodide (WebAssembly) mode.
|
|
287
|
+
|
|
288
|
+
Note:
|
|
289
|
+
This property is read-only.
|
|
283
290
|
"""
|
|
284
291
|
|
|
285
292
|
platform_brightness: Optional[Brightness] = None
|
|
@@ -295,7 +302,7 @@ class Page(BasePage):
|
|
|
295
302
|
IP address of the connected user.
|
|
296
303
|
|
|
297
304
|
Note:
|
|
298
|
-
This property is web only.
|
|
305
|
+
This property is web- and read-only only.
|
|
299
306
|
"""
|
|
300
307
|
|
|
301
308
|
client_user_agent: Optional[str] = None
|
|
@@ -303,7 +310,12 @@ class Page(BasePage):
|
|
|
303
310
|
Browser details of the connected user.
|
|
304
311
|
|
|
305
312
|
Note:
|
|
306
|
-
This property is web only.
|
|
313
|
+
This property is web- and read-only only.
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
platform: Optional[PagePlatform] = None
|
|
317
|
+
"""
|
|
318
|
+
The operating system the application is running on.
|
|
307
319
|
"""
|
|
308
320
|
|
|
309
321
|
fonts: Optional[dict[str, str]] = None
|
|
@@ -400,6 +412,8 @@ class Page(BasePage):
|
|
|
400
412
|
"""
|
|
401
413
|
TBD
|
|
402
414
|
"""
|
|
415
|
+
_services: list[Service] = field(default_factory=list)
|
|
416
|
+
_user_services: ServiceRegistry = field(default_factory=lambda: ServiceRegistry())
|
|
403
417
|
|
|
404
418
|
def __post_init__(
|
|
405
419
|
self,
|
|
@@ -420,7 +434,6 @@ class Page(BasePage):
|
|
|
420
434
|
]
|
|
421
435
|
self.__last_route = None
|
|
422
436
|
self.__query: QueryString = QueryString(self)
|
|
423
|
-
self.__session_storage: SessionStorage = SessionStorage()
|
|
424
437
|
self.__authorization: Optional[Authorization] = None
|
|
425
438
|
|
|
426
439
|
def get_control(self, id: int) -> Optional[BaseControl]:
|
|
@@ -442,7 +455,37 @@ class Page(BasePage):
|
|
|
442
455
|
ft.run(main)
|
|
443
456
|
```
|
|
444
457
|
"""
|
|
445
|
-
return self.
|
|
458
|
+
return self.session.index.get(id)
|
|
459
|
+
|
|
460
|
+
def render(
|
|
461
|
+
self,
|
|
462
|
+
component: Callable[..., Union[list[View], View, list[Control], Control]],
|
|
463
|
+
*args,
|
|
464
|
+
**kwargs,
|
|
465
|
+
):
|
|
466
|
+
logger.debug("Page.render()")
|
|
467
|
+
self._notify = self.__notify
|
|
468
|
+
self.views[0].controls = Renderer().render(component, *args, **kwargs)
|
|
469
|
+
self.__render()
|
|
470
|
+
|
|
471
|
+
def render_views(
|
|
472
|
+
self,
|
|
473
|
+
component: Callable[..., Union[list[View], View, list[Control], Control]],
|
|
474
|
+
*args,
|
|
475
|
+
**kwargs,
|
|
476
|
+
):
|
|
477
|
+
logger.debug("Page.render_views()")
|
|
478
|
+
self._notify = self.__notify
|
|
479
|
+
self.views = Renderer().render(component, *args, **kwargs)
|
|
480
|
+
self.__render()
|
|
481
|
+
|
|
482
|
+
def __render(self):
|
|
483
|
+
self.update()
|
|
484
|
+
context.enable_components_mode()
|
|
485
|
+
self.session.start_updates_scheduler()
|
|
486
|
+
|
|
487
|
+
def schedule_update(self):
|
|
488
|
+
self.session.schedule_update(self)
|
|
446
489
|
|
|
447
490
|
def update(self, *controls) -> None:
|
|
448
491
|
if len(controls) == 0:
|
|
@@ -450,17 +493,15 @@ class Page(BasePage):
|
|
|
450
493
|
else:
|
|
451
494
|
self.__update(*controls)
|
|
452
495
|
|
|
453
|
-
def
|
|
454
|
-
|
|
455
|
-
return sess
|
|
456
|
-
raise Exception("An attempt to fetch destroyed session.")
|
|
496
|
+
def __notify(self, name: str, value: Any):
|
|
497
|
+
self.schedule_update()
|
|
457
498
|
|
|
458
499
|
def __update(self, *controls: Control):
|
|
459
500
|
for c in controls:
|
|
460
|
-
self.
|
|
501
|
+
self.session.patch_control(c)
|
|
461
502
|
|
|
462
503
|
def error(self, message: str) -> None:
|
|
463
|
-
self.
|
|
504
|
+
self.session.error(message)
|
|
464
505
|
|
|
465
506
|
def before_event(self, e: ControlEvent):
|
|
466
507
|
if isinstance(e, RouteChangeEvent):
|
|
@@ -468,12 +509,15 @@ class Page(BasePage):
|
|
|
468
509
|
return False
|
|
469
510
|
self.__last_route = e.route
|
|
470
511
|
self.query()
|
|
512
|
+
|
|
471
513
|
elif isinstance(e, ViewPopEvent):
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
514
|
+
views = unwrap_component(self.views)
|
|
515
|
+
view_index = next(
|
|
516
|
+
(i for i, v in enumerate(views) if v.route == e.route), None
|
|
517
|
+
)
|
|
518
|
+
if view_index is not None:
|
|
519
|
+
e.view = views[view_index]
|
|
520
|
+
|
|
477
521
|
return super().before_event(e)
|
|
478
522
|
|
|
479
523
|
def run_task(
|
|
@@ -491,7 +535,7 @@ class Page(BasePage):
|
|
|
491
535
|
raise TypeError("handler must be a coroutine function")
|
|
492
536
|
|
|
493
537
|
future = asyncio.run_coroutine_threadsafe(
|
|
494
|
-
handler(*args, **kwargs), self.
|
|
538
|
+
handler(*args, **kwargs), self.session.connection.loop
|
|
495
539
|
)
|
|
496
540
|
|
|
497
541
|
def _on_completion(f):
|
|
@@ -527,13 +571,14 @@ class Page(BasePage):
|
|
|
527
571
|
if is_pyodide():
|
|
528
572
|
handler_with_context(*args, **kwargs)
|
|
529
573
|
else:
|
|
530
|
-
loop = self.
|
|
574
|
+
loop = self.session.connection.loop
|
|
531
575
|
loop.call_soon_threadsafe(
|
|
532
576
|
loop.run_in_executor,
|
|
533
577
|
self.executor,
|
|
534
578
|
partial(handler_with_context, *args, **kwargs),
|
|
535
579
|
)
|
|
536
580
|
|
|
581
|
+
@deprecated("Use push_route() instead.", version="0.70.0", show_parentheses=True)
|
|
537
582
|
def go(
|
|
538
583
|
self, route: str, skip_route_change_event: bool = False, **kwargs: Any
|
|
539
584
|
) -> None:
|
|
@@ -542,20 +587,76 @@ class Page(BasePage):
|
|
|
542
587
|
[`page.on_route_change`](#on_route_change) event handler to update views and
|
|
543
588
|
finally calls `page.update()`.
|
|
544
589
|
"""
|
|
545
|
-
self.route = route if not kwargs else route + self.query.post(kwargs)
|
|
546
590
|
|
|
547
|
-
|
|
548
|
-
e = RouteChangeEvent(
|
|
549
|
-
name="route_change", control=self, data=self.route, route=self.route
|
|
550
|
-
)
|
|
551
|
-
if self.on_route_change:
|
|
552
|
-
if asyncio.iscoroutinefunction(self.on_route_change):
|
|
553
|
-
asyncio.create_task(self.on_route_change(e))
|
|
554
|
-
elif callable(self.on_route_change):
|
|
555
|
-
self.on_route_change(e)
|
|
591
|
+
self.push_route(route, **kwargs)
|
|
556
592
|
|
|
557
|
-
|
|
558
|
-
|
|
593
|
+
def push_route(self, route: str, **kwargs: Any) -> None:
|
|
594
|
+
"""
|
|
595
|
+
Pushes a new navigation route to the browser history stack.
|
|
596
|
+
Changing route will fire [`page.on_route_change`](#on_route_change) event
|
|
597
|
+
handler.
|
|
598
|
+
|
|
599
|
+
Example:
|
|
600
|
+
|
|
601
|
+
```python
|
|
602
|
+
import flet as ft
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def main(page: ft.Page):
|
|
606
|
+
page.title = "Push Route Example"
|
|
607
|
+
|
|
608
|
+
def route_change(e):
|
|
609
|
+
page.views.clear()
|
|
610
|
+
page.views.append(
|
|
611
|
+
ft.View(
|
|
612
|
+
route="/",
|
|
613
|
+
controls=[
|
|
614
|
+
ft.AppBar(title=ft.Text("Flet app")),
|
|
615
|
+
ft.ElevatedButton(
|
|
616
|
+
"Visit Store",
|
|
617
|
+
on_click=lambda _: page.push_route("/store"),
|
|
618
|
+
),
|
|
619
|
+
],
|
|
620
|
+
)
|
|
621
|
+
)
|
|
622
|
+
if page.route == "/store":
|
|
623
|
+
page.views.append(
|
|
624
|
+
ft.View(
|
|
625
|
+
route="/store",
|
|
626
|
+
can_pop=True,
|
|
627
|
+
controls=[
|
|
628
|
+
ft.AppBar(title=ft.Text("Store")),
|
|
629
|
+
ft.ElevatedButton(
|
|
630
|
+
"Go Home", on_click=lambda _: page.push_route("/")
|
|
631
|
+
),
|
|
632
|
+
],
|
|
633
|
+
)
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
def view_pop(e):
|
|
637
|
+
page.views.pop()
|
|
638
|
+
top_view = page.views[-1]
|
|
639
|
+
page.push_route(top_view.route)
|
|
640
|
+
|
|
641
|
+
page.on_route_change = route_change
|
|
642
|
+
page.on_view_pop = view_pop
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
ft.run(main)
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
route: New navigation route.
|
|
650
|
+
**kwargs: Additional query string parameters to be added to the route.
|
|
651
|
+
"""
|
|
652
|
+
|
|
653
|
+
new_route = route if not kwargs else route + self.query.post(kwargs)
|
|
654
|
+
asyncio.create_task(
|
|
655
|
+
self._invoke_method(
|
|
656
|
+
"push_route",
|
|
657
|
+
arguments={"route": new_route},
|
|
658
|
+
)
|
|
659
|
+
)
|
|
559
660
|
|
|
560
661
|
def get_upload_url(self, file_name: str, expires: int) -> str:
|
|
561
662
|
"""
|
|
@@ -577,7 +678,7 @@ class Page(BasePage):
|
|
|
577
678
|
ft.run(main, upload_dir="uploads")
|
|
578
679
|
```
|
|
579
680
|
"""
|
|
580
|
-
return self.
|
|
681
|
+
return self.session.connection.get_upload_url(file_name, expires)
|
|
581
682
|
|
|
582
683
|
async def login(
|
|
583
684
|
self,
|
|
@@ -613,9 +714,9 @@ class Page(BasePage):
|
|
|
613
714
|
if redirect_to_page:
|
|
614
715
|
up = urlparse(provider.redirect_url)
|
|
615
716
|
auth_attrs["completePageUrl"] = up._replace(
|
|
616
|
-
path=f"{self.
|
|
717
|
+
path=f"{self.session.connection.page_name}{self.route}"
|
|
617
718
|
).geturl()
|
|
618
|
-
self.
|
|
719
|
+
self.session.connection.oauth_authorize(auth_attrs)
|
|
619
720
|
if on_open_authorization_url:
|
|
620
721
|
await on_open_authorization_url(authorization_url)
|
|
621
722
|
else:
|
|
@@ -742,42 +843,60 @@ class Page(BasePage):
|
|
|
742
843
|
"""
|
|
743
844
|
await self.url_launcher.close_in_app_web_view()
|
|
744
845
|
|
|
745
|
-
|
|
846
|
+
@property
|
|
847
|
+
def session(self) -> "Session":
|
|
848
|
+
"""
|
|
849
|
+
The session that this page belongs to.
|
|
850
|
+
"""
|
|
851
|
+
if sess := self.__session():
|
|
852
|
+
return sess
|
|
853
|
+
raise Exception("An attempt to fetch destroyed session.")
|
|
854
|
+
|
|
746
855
|
@property
|
|
747
856
|
def query(self) -> QueryString:
|
|
857
|
+
"""
|
|
858
|
+
The query parameters of the current page.
|
|
859
|
+
"""
|
|
748
860
|
return self.__query
|
|
749
861
|
|
|
750
|
-
# url
|
|
751
862
|
@property
|
|
752
863
|
def url(self) -> Optional[str]:
|
|
753
|
-
|
|
864
|
+
"""
|
|
865
|
+
The URL of the current page.
|
|
866
|
+
"""
|
|
867
|
+
return self.session.connection.page_url
|
|
754
868
|
|
|
755
|
-
# name
|
|
756
869
|
@property
|
|
757
870
|
def name(self) -> str:
|
|
758
|
-
|
|
871
|
+
"""
|
|
872
|
+
The name of the current page.
|
|
873
|
+
"""
|
|
874
|
+
return self.session.connection.page_name
|
|
759
875
|
|
|
760
|
-
# loop
|
|
761
876
|
@property
|
|
762
877
|
def loop(self) -> asyncio.AbstractEventLoop:
|
|
763
|
-
|
|
878
|
+
"""
|
|
879
|
+
The event loop for the current page.
|
|
880
|
+
"""
|
|
881
|
+
return self.session.connection.loop
|
|
764
882
|
|
|
765
|
-
# executor
|
|
766
883
|
@property
|
|
767
884
|
def executor(self) -> Optional[ThreadPoolExecutor]:
|
|
768
|
-
|
|
885
|
+
"""
|
|
886
|
+
The executor for the current page.
|
|
887
|
+
"""
|
|
888
|
+
return self.session.connection.executor
|
|
769
889
|
|
|
770
|
-
# auth
|
|
771
890
|
@property
|
|
772
891
|
def auth(self) -> Optional[Authorization]:
|
|
892
|
+
"""
|
|
893
|
+
The current authorization context, or `None` if the user is not authorized.
|
|
894
|
+
"""
|
|
773
895
|
return self.__authorization
|
|
774
896
|
|
|
775
|
-
# pubsub
|
|
776
897
|
@property
|
|
777
898
|
def pubsub(self) -> "PubSubClient":
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
def session(self) -> SessionStorage:
|
|
783
|
-
return self.__session_storage
|
|
899
|
+
"""
|
|
900
|
+
The PubSub client for the current page.
|
|
901
|
+
"""
|
|
902
|
+
return self.session.pubsub_client
|
|
@@ -8,9 +8,6 @@ __all__ = ["HapticFeedback"]
|
|
|
8
8
|
class HapticFeedback(Service):
|
|
9
9
|
"""
|
|
10
10
|
Allows access to the haptic feedback interface on the device.
|
|
11
|
-
|
|
12
|
-
It is non-visual and should be added to
|
|
13
|
-
[`Page.services`][flet.] list before it can be used.
|
|
14
11
|
"""
|
|
15
12
|
|
|
16
13
|
async def heavy_impact(self):
|
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import Any, Optional
|
|
9
9
|
|
|
10
10
|
import msgpack
|
|
11
|
+
|
|
11
12
|
from flet.controls.base_control import BaseControl
|
|
12
13
|
from flet.messaging.connection import Connection
|
|
13
14
|
from flet.messaging.protocol import (
|
|
@@ -236,17 +237,23 @@ class FletSocketServer(Connection):
|
|
|
236
237
|
|
|
237
238
|
logger.debug("Cancelling pending tasks...")
|
|
238
239
|
|
|
239
|
-
tasks = [
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
tasks = [
|
|
241
|
+
task
|
|
242
|
+
for task in [
|
|
243
|
+
self.__receive_loop_task,
|
|
244
|
+
self.__send_loop_task,
|
|
245
|
+
self.__serve_task,
|
|
246
|
+
]
|
|
247
|
+
if task
|
|
248
|
+
]
|
|
244
249
|
|
|
245
250
|
for task in tasks:
|
|
246
251
|
task.cancel()
|
|
247
252
|
|
|
248
253
|
try:
|
|
249
|
-
await asyncio.wait_for(
|
|
254
|
+
await asyncio.wait_for(
|
|
255
|
+
asyncio.gather(*tasks, return_exceptions=True), timeout=1.0
|
|
256
|
+
)
|
|
250
257
|
except asyncio.TimeoutError:
|
|
251
258
|
logger.warning("Some tasks did not exit in time, skipping.")
|
|
252
259
|
except asyncio.CancelledError:
|