flet 1.0.0.dev2__tar.gz → 1.0.1__tar.gz
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.
- {flet-1.0.0.dev2 → flet-1.0.1}/PKG-INFO +14 -13
- {flet-1.0.0.dev2 → flet-1.0.1}/README.md +5 -4
- {flet-1.0.0.dev2 → flet-1.0.1}/pyproject.toml +9 -9
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/app.py +24 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/base_control.py +23 -8
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/icon.py +5 -2
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/raw_image.py +7 -1
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/object_patch.py +10 -6
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/page.py +110 -18
- flet-1.0.1/src/flet/controls/services/service.py +54 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/messaging/session.py +16 -7
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/testing/flet_test_app.py +14 -7
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/version.py +1 -1
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet.egg-info/PKG-INFO +14 -13
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet.egg-info/SOURCES.txt +2 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet.egg-info/requires.txt +8 -8
- flet-1.0.1/tests/test_app_logging.py +53 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_component_diff.py +93 -0
- flet-1.0.1/tests/test_icons.py +105 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_raw_image_pixels.py +56 -0
- flet-1.0.1/tests/test_service_registration.py +705 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_skip_double_update.py +12 -4
- flet-1.0.0.dev2/src/flet/controls/services/service.py +0 -19
- flet-1.0.0.dev2/tests/test_icons.py +0 -27
- {flet-1.0.0.dev2 → flet-1.0.1}/setup.cfg +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/authorization.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/authorization_service.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/group.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/oauth_provider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/oauth_token.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/providers/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/providers/auth0_oauth_provider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/providers/azure_oauth_provider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/providers/github_oauth_provider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/providers/google_oauth_provider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/auth/user.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/canvas/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/cli.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/component.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/component_decorator.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/component_owned.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/hook.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/use_callback.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/use_context.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/use_dialog.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/use_effect.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/use_memo.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/use_ref.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/hooks/use_state.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/memo.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/observable.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/public_utils.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/router.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/components/utils.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/action_control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/adaptive_control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/alignment.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/animation.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/base_page.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/blur.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/border.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/border_radius.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/box.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/buttons.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/client_action.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/colors.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/context.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/control_event.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/control_state.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/animated_switcher.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/autofill_group.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/arc.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/canvas.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/circle.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/color.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/fill.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/image.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/line.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/oval.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/path.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/points.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/rect.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/shadow.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/shape.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/canvas/text.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/column.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/dismissible.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/drag_target.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/draggable.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/flet_app.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/gesture_detector.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/grid_view.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/hero.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/image.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/interactive_viewer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/keyboard_listener.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/list_view.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/markdown.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/merge_semantics.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/page_view.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/pagelet.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/placeholder.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/reorderable_drag_handle.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/responsive_row.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/rotated_box.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/row.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/safe_area.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/screenshot.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/semantics.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/shader_mask.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/shimmer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/stack.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/text.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/text_span.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/transparent_pointer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/view.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/window.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/core/window_drag_area.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_action_sheet.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_action_sheet_action.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_activity_indicator.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_alert_dialog.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_app_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_bottom_sheet.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_checkbox.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_colors.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_context_menu.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_context_menu_action.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_date_picker.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_dialog_action.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_filled_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_icons.json +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_icons.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_icons.pyi +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_list_tile.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_navigation_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_picker.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_radio.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_segmented_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_slider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_sliding_segmented_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_switch.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_textfield.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_timer_picker.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/cupertino/cupertino_tinted_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/device_info.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/dialog_control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/duration.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/embed_json_encoder.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/events.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/exceptions.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/geometry.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/gradients.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/icon_data.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/id_counter.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/keys.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/layout_control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/margin.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/alert_dialog.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/app_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/auto_complete.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/badge.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/banner.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/bottom_app_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/bottom_sheet.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/card.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/checkbox.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/chip.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/circle_avatar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/container.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/context_menu.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/datatable.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/date_picker.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/date_range_picker.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/divider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/dropdown.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/dropdownm2.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/expansion_panel.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/expansion_tile.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/filled_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/filled_tonal_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/floating_action_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/form_field_control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/icon_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/icons.json +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/icons.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/icons.pyi +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/list_tile.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/menu_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/menu_item_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/navigation_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/navigation_drawer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/navigation_rail.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/outlined_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/popup_menu_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/progress_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/progress_ring.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/radio.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/radio_group.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/range_slider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/reorderable_list_view.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/search_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/segmented_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/selection_area.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/slider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/snack_bar.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/submenu_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/switch.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/tabs.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/text_button.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/textfield.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/time_picker.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/tooltip.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/material/vertical_divider.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/multi_view.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/padding.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/painting.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/query_string.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/ref.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/scrollable_control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/accelerometer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/barometer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/battery.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/browser_context_menu.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/clipboard.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/connectivity.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/file_picker.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/gyroscope.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/haptic_feedback.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/magnetometer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/screen_brightness.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/semantics_service.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/sensor_error_event.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/shake_detector.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/share.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/shared_preferences.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/storage_paths.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/url_launcher.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/user_accelerometer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/services/wakelock.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/template_route.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/text_style.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/theme.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/transform.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/types.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/controls/value_types.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/data_channel.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/fastapi/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/messaging/connection.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/messaging/flet_dart_bridge_server.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/messaging/flet_socket_server.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/messaging/protocol.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/messaging/pyodide_connection.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/messaging/session_store.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/pubsub/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/pubsub/pubsub_client.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/pubsub/pubsub_hub.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/py.typed +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/pytest_plugin.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/security/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/testing/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/testing/finder.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/testing/remote_tester.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/testing/tester.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/__init__.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/browser.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/classproperty.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/deprecated.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/deprecated_enum.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/environment.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/files.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/from_dict.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/griffe_deprecations.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/hashing.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/json_utils.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/locks.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/network.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/object_model.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/once.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/pip.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/platform_utils.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/slugify.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/strings.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/typing_utils.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/validation.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet/utils/vector.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet.egg-info/dependency_links.txt +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet.egg-info/entry_points.txt +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/src/flet.egg-info/top_level.txt +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_assets_dir_resolution.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_auth_lazy_imports.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_base_control.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_client_actions.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_colors.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_component_effects.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_component_owned_identity.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_component_renderer.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_context_auto_update.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_dataclasses.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_datetime.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_deprecated.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_environment.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_events.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_files.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_from_dict.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_generator_handler_yield.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_griffe_deprecations.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_object_diff_frozen.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_object_diff_in_place.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_object_diff_memory_churn.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_object_diff_performance.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_observable.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_patch_dataclass.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_router_modal.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_router_pop.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_router_recursive.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_session_disconnect_buffering.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_use_dialog.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_validation.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_validation_benchmark.py +0 -0
- {flet-1.0.0.dev2 → flet-1.0.1}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Flet for Python - easily build interactive multi-platform apps in Python
|
|
5
5
|
Author-email: "Appveyor Systems Inc." <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -9,23 +9,23 @@ Project-URL: Repository, https://github.com/flet-dev/flet
|
|
|
9
9
|
Project-URL: Documentation, https://flet.dev/docs/
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist: flet-cli==1.0.
|
|
13
|
-
Requires-Dist: flet-web==1.0.
|
|
12
|
+
Requires-Dist: flet-cli==1.0.1; extra == "cli"
|
|
13
|
+
Requires-Dist: flet-web==1.0.1; extra == "web"
|
|
14
14
|
Requires-Dist: oauthlib>=3.2.2; platform_system != "Emscripten"
|
|
15
15
|
Requires-Dist: httpx>=0.28.1; platform_system != "Emscripten"
|
|
16
16
|
Requires-Dist: repath>=0.9.0
|
|
17
17
|
Requires-Dist: msgpack>=1.1.0
|
|
18
18
|
Requires-Dist: typing-extensions; python_version < "3.11"
|
|
19
19
|
Provides-Extra: all
|
|
20
|
-
Requires-Dist: flet-cli==1.0.
|
|
21
|
-
Requires-Dist: flet-web==1.0.
|
|
22
|
-
Requires-Dist: flet-desktop==1.0.
|
|
20
|
+
Requires-Dist: flet-cli==1.0.1; extra == "all"
|
|
21
|
+
Requires-Dist: flet-web==1.0.1; extra == "all"
|
|
22
|
+
Requires-Dist: flet-desktop==1.0.1; extra == "all"
|
|
23
23
|
Provides-Extra: cli
|
|
24
|
-
Requires-Dist: flet-cli==1.0.
|
|
24
|
+
Requires-Dist: flet-cli==1.0.1; extra == "cli"
|
|
25
25
|
Provides-Extra: desktop
|
|
26
|
-
Requires-Dist: flet-desktop==1.0.
|
|
26
|
+
Requires-Dist: flet-desktop==1.0.1; extra == "desktop"
|
|
27
27
|
Provides-Extra: web
|
|
28
|
-
Requires-Dist: flet-web==1.0.
|
|
28
|
+
Requires-Dist: flet-web==1.0.1; extra == "web"
|
|
29
29
|
Provides-Extra: test
|
|
30
30
|
Requires-Dist: pytest>=7.2.0; extra == "test"
|
|
31
31
|
Requires-Dist: pytest-asyncio>=1.1.0; extra == "test"
|
|
@@ -41,7 +41,7 @@ Requires-Dist: scikit-image>=0.25.2; extra == "test"
|
|
|
41
41
|
</p>
|
|
42
42
|
|
|
43
43
|
<p align="center">
|
|
44
|
-
<em>Build
|
|
44
|
+
<em>Build cross-platform apps in Python. No frontend experience required.</em>
|
|
45
45
|
</p>
|
|
46
46
|
|
|
47
47
|
<p align="center">
|
|
@@ -59,10 +59,11 @@ Requires-Dist: scikit-image>=0.25.2; extra == "test"
|
|
|
59
59
|
<img src="https://flet.dev/docs/assets/badges/docs-coverage/flet.svg" alt="Docstring coverage" /></a>
|
|
60
60
|
</p>
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Flet is a Python framework for building web, desktop, and mobile apps without prior experience in frontend development.
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
> **[Try Flet online in Flet Studio](https://studio.flet.dev)**
|
|
65
|
+
>
|
|
66
|
+
> Create, run, and share Python apps in your browser. Start from an example, write your own code, or get help from the AI agent. No installation required.
|
|
66
67
|
|
|
67
68
|
### <img src="https://flet.dev/img/pages/home/single-code-base.svg" width="25" align="top" /> Single code base for any device
|
|
68
69
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
<em>Build
|
|
9
|
+
<em>Build cross-platform apps in Python. No frontend experience required.</em>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
<img src="https://flet.dev/docs/assets/badges/docs-coverage/flet.svg" alt="Docstring coverage" /></a>
|
|
25
25
|
</p>
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Flet is a Python framework for building web, desktop, and mobile apps without prior experience in frontend development.
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
> **[Try Flet online in Flet Studio](https://studio.flet.dev)**
|
|
30
|
+
>
|
|
31
|
+
> Create, run, and share Python apps in your browser. Start from an example, write your own code, or get help from the AI agent. No installation required.
|
|
31
32
|
|
|
32
33
|
### <img src="https://flet.dev/img/pages/home/single-code-base.svg" width="25" align="top" /> Single code base for any device
|
|
33
34
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flet"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.1"
|
|
4
4
|
description = "Flet for Python - easily build interactive multi-platform apps in Python"
|
|
5
5
|
authors = [{name = "Appveyor Systems Inc.", email = "hello@flet.dev"}]
|
|
6
6
|
license = "Apache-2.0"
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"flet-cli==1.0.
|
|
11
|
-
"flet-web==1.0.
|
|
10
|
+
"flet-cli==1.0.1; extra == 'cli'",
|
|
11
|
+
"flet-web==1.0.1; extra == 'web'",
|
|
12
12
|
"oauthlib >=3.2.2; platform_system != 'Emscripten'",
|
|
13
13
|
"httpx >=0.28.1; platform_system != 'Emscripten'",
|
|
14
14
|
"repath >=0.9.0",
|
|
@@ -23,15 +23,15 @@ Documentation = "https://flet.dev/docs/"
|
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
25
|
all = [
|
|
26
|
-
"flet-cli==1.0.
|
|
27
|
-
"flet-web==1.0.
|
|
28
|
-
"flet-desktop==1.0.
|
|
26
|
+
"flet-cli==1.0.1",
|
|
27
|
+
"flet-web==1.0.1",
|
|
28
|
+
"flet-desktop==1.0.1",
|
|
29
29
|
]
|
|
30
|
-
cli = ["flet-cli==1.0.
|
|
30
|
+
cli = ["flet-cli==1.0.1"]
|
|
31
31
|
desktop = [
|
|
32
|
-
"flet-desktop==1.0.
|
|
32
|
+
"flet-desktop==1.0.1",
|
|
33
33
|
]
|
|
34
|
-
web = ["flet-web==1.0.
|
|
34
|
+
web = ["flet-web==1.0.1"]
|
|
35
35
|
# Dependencies needed to run integration tests written with `flet.testing`
|
|
36
36
|
# (the `flet test` command / the pytest plugin). Pillow/numpy/scikit-image
|
|
37
37
|
# back screenshot and GIF comparison.
|
|
@@ -45,6 +45,26 @@ Used for both `main` and `before_main` handlers.
|
|
|
45
45
|
"""
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def __configure_logging_from_env() -> None:
|
|
49
|
+
"""
|
|
50
|
+
Applies the log level named by the `FLET_LOG_LEVEL` environment variable.
|
|
51
|
+
|
|
52
|
+
`flet run -v` sets this so the framework's own logging reaches the console
|
|
53
|
+
without the app having to configure it. `basicConfig` does nothing once the
|
|
54
|
+
root logger has a handler, so an app that configures logging itself keeps
|
|
55
|
+
the setup it chose.
|
|
56
|
+
"""
|
|
57
|
+
level = os.getenv("FLET_LOG_LEVEL")
|
|
58
|
+
if not level:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
resolved = logging.getLevelName(level.strip().upper())
|
|
62
|
+
if isinstance(resolved, int):
|
|
63
|
+
logging.basicConfig(level=resolved)
|
|
64
|
+
else:
|
|
65
|
+
logger.warning("Unknown FLET_LOG_LEVEL: %s", level)
|
|
66
|
+
|
|
67
|
+
|
|
48
68
|
def run(
|
|
49
69
|
main: AppCallable,
|
|
50
70
|
before_main: Optional[AppCallable] = None,
|
|
@@ -83,6 +103,8 @@ def run(
|
|
|
83
103
|
A FastAPI ASGI app when `export_asgi_app=True`.
|
|
84
104
|
Otherwise, runs the app and returns `None`.
|
|
85
105
|
"""
|
|
106
|
+
__configure_logging_from_env()
|
|
107
|
+
|
|
86
108
|
if is_pyodide():
|
|
87
109
|
__run_pyodide(main=main, before_main=before_main)
|
|
88
110
|
return
|
|
@@ -156,6 +178,8 @@ async def run_async(
|
|
|
156
178
|
no_cdn: Whether to avoid loading CanvasKit, Pyodide, and fonts from CDN.
|
|
157
179
|
"""
|
|
158
180
|
|
|
181
|
+
__configure_logging_from_env()
|
|
182
|
+
|
|
159
183
|
if is_pyodide():
|
|
160
184
|
__run_pyodide(main=main, before_main=before_main)
|
|
161
185
|
return
|
|
@@ -328,6 +328,9 @@ class BaseControl:
|
|
|
328
328
|
|
|
329
329
|
Override this hook to perform lightweight setup that depends on initialized
|
|
330
330
|
fields. Do not call `update()` here.
|
|
331
|
+
|
|
332
|
+
See :class:`~flet.Service` for service registration timing and page access
|
|
333
|
+
during this hook.
|
|
331
334
|
"""
|
|
332
335
|
pass
|
|
333
336
|
|
|
@@ -395,22 +398,34 @@ class BaseControl:
|
|
|
395
398
|
|
|
396
399
|
def get_data_channel(self, channel_id: int):
|
|
397
400
|
"""
|
|
398
|
-
Resolve the
|
|
399
|
-
|
|
401
|
+
Resolve the :class:`~flet.DataChannel` allocated on the Dart side for this
|
|
402
|
+
control or service.
|
|
403
|
+
|
|
404
|
+
Example:
|
|
405
|
+
A custom service whose Dart implementation opens a data channel and
|
|
406
|
+
raises `data_channel_open` can capture it during initialization:
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
import flet as ft
|
|
400
410
|
|
|
401
|
-
on_data_channel_open: Optional[ft.EventHandler[DataChannelOpenEvent]] = None
|
|
402
411
|
|
|
403
|
-
|
|
404
|
-
|
|
412
|
+
@ft.control("MyService")
|
|
413
|
+
class MyService(ft.Service):
|
|
414
|
+
on_data_channel_open: ft.EventHandler[ft.DataChannelOpenEvent] | None = None
|
|
405
415
|
|
|
406
|
-
|
|
407
|
-
|
|
416
|
+
def init(self):
|
|
417
|
+
super().init()
|
|
418
|
+
self.on_data_channel_open = self._on_open
|
|
419
|
+
|
|
420
|
+
def _on_open(self, e: ft.DataChannelOpenEvent):
|
|
421
|
+
self._channel = self.get_data_channel(e.channel_id)
|
|
422
|
+
```
|
|
408
423
|
|
|
409
424
|
Idempotent — the underlying Connection caches DataChannels by id,
|
|
410
425
|
so repeated calls return the same instance. No error path: the id
|
|
411
426
|
always comes from a framework-fired event, so by the time this
|
|
412
427
|
runs the channel exists on both sides.
|
|
413
|
-
"""
|
|
428
|
+
""" # noqa: E501
|
|
414
429
|
return self.page.session.connection.data_channel_for(channel_id)
|
|
415
430
|
|
|
416
431
|
# public methods
|
|
@@ -27,8 +27,11 @@ class Icon(LayoutControl):
|
|
|
27
27
|
"""
|
|
28
28
|
The icon to display, selected from a predefined icon set.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
[
|
|
30
|
+
Browse every available icon on the
|
|
31
|
+
[Icons](https://flet.dev/docs/types/icons) and
|
|
32
|
+
[CupertinoIcons](https://flet.dev/docs/types/cupertinoicons) pages, or run the
|
|
33
|
+
[icons browser](https://studio.flet.dev/gallery/run/apps/icons_browser/) as a
|
|
34
|
+
live Flet app.
|
|
32
35
|
"""
|
|
33
36
|
|
|
34
37
|
color: Optional[ColorValue] = None
|
|
@@ -154,7 +154,13 @@ class RawImage(LayoutControl):
|
|
|
154
154
|
def _capture_channel(self, e: DataChannelOpenEvent) -> None:
|
|
155
155
|
# Single-channel widget; no need to dispatch on e.channel_name.
|
|
156
156
|
self._channel = self.get_data_channel(e.channel_id)
|
|
157
|
-
|
|
157
|
+
loop = asyncio.get_running_loop()
|
|
158
|
+
# Embedded DataChannel callbacks arrive on a native bridge thread.
|
|
159
|
+
# Resolve acknowledgements on the event loop so Future callbacks wake
|
|
160
|
+
# the renderer immediately and the pending-ack queue stays on one thread.
|
|
161
|
+
self._channel.on_bytes(
|
|
162
|
+
lambda payload: loop.call_soon_threadsafe(self._on_dart_message, payload)
|
|
163
|
+
)
|
|
158
164
|
# Acks pending on a previous channel will never arrive; resolve
|
|
159
165
|
# them so old `render` awaits return instead of hanging.
|
|
160
166
|
while self._pending_acks:
|
|
@@ -1015,12 +1015,8 @@ class DiffBuilder:
|
|
|
1015
1015
|
new_item, "fn", None
|
|
1016
1016
|
)
|
|
1017
1017
|
|
|
1018
|
-
if
|
|
1019
|
-
frozen_local
|
|
1020
|
-
and old_item is not new_item
|
|
1021
|
-
and same_type
|
|
1022
|
-
and same_component_fn
|
|
1023
|
-
and _keys_match()
|
|
1018
|
+
if old_item is new_item or (
|
|
1019
|
+
frozen_local and same_type and same_component_fn and _keys_match()
|
|
1024
1020
|
):
|
|
1025
1021
|
self._compare_dataclasses(
|
|
1026
1022
|
parent, _path_join(path, idx), old_item, new_item, frozen_local
|
|
@@ -1138,6 +1134,14 @@ class DiffBuilder:
|
|
|
1138
1134
|
"""
|
|
1139
1135
|
logger.debug("\n_compare_dataclasses: %s\n\n%s\n%s\n", path, src, dst)
|
|
1140
1136
|
|
|
1137
|
+
if frozen and src is dst:
|
|
1138
|
+
# Retained immutable children have no new description to reconcile.
|
|
1139
|
+
# In particular, a Component must not migrate state to itself or
|
|
1140
|
+
# render over its old body before that body can be diffed.
|
|
1141
|
+
if parent is not None and parent is not dst:
|
|
1142
|
+
dst._parent = weakref.ref(parent)
|
|
1143
|
+
return
|
|
1144
|
+
|
|
1141
1145
|
if (
|
|
1142
1146
|
self.control_cls
|
|
1143
1147
|
and isinstance(parent, self.control_cls)
|
|
@@ -6,12 +6,13 @@ import threading
|
|
|
6
6
|
import weakref
|
|
7
7
|
from collections.abc import Awaitable, Coroutine
|
|
8
8
|
from concurrent.futures import CancelledError, Future, ThreadPoolExecutor
|
|
9
|
-
from dataclasses import InitVar, dataclass, field
|
|
9
|
+
from dataclasses import InitVar, dataclass, field, fields, is_dataclass
|
|
10
10
|
from functools import partial
|
|
11
11
|
from typing import (
|
|
12
12
|
TYPE_CHECKING,
|
|
13
13
|
Any,
|
|
14
14
|
Callable,
|
|
15
|
+
ClassVar,
|
|
15
16
|
Optional,
|
|
16
17
|
ParamSpec,
|
|
17
18
|
TypeVar,
|
|
@@ -103,28 +104,31 @@ class ServiceRegistry(Service):
|
|
|
103
104
|
Tracked service instances currently attached to this page.
|
|
104
105
|
"""
|
|
105
106
|
|
|
107
|
+
_auto_register: ClassVar[bool] = False
|
|
108
|
+
"""
|
|
109
|
+
Disabled so each registry belongs only to its own page. An embedded `FletApp`
|
|
110
|
+
can construct a page while the host page is the current context; automatic
|
|
111
|
+
registration would incorrectly add the new registry to the host's services.
|
|
112
|
+
The client has no service binding for type "ServiceRegistry".
|
|
113
|
+
"""
|
|
114
|
+
|
|
106
115
|
def __post_init__(self, ref: Optional[Ref[Any]]):
|
|
107
116
|
super().__post_init__(ref)
|
|
108
117
|
self._internals["uid"] = random_string(10)
|
|
109
118
|
self._lock: threading.Lock = threading.Lock()
|
|
110
119
|
|
|
111
|
-
def init(self):
|
|
112
|
-
# Deliberately skips Service.init(), which registers the instance into
|
|
113
|
-
# `context.page._services`. A registry is the *container* for services,
|
|
114
|
-
# not a service itself, and self-registering leaks across pages: when a
|
|
115
|
-
# second Page is constructed while another page is still the current
|
|
116
|
-
# context — an embedded `FletApp` inside a host app — the new page's
|
|
117
|
-
# registry is registered inside the HOST's registry. The client has no
|
|
118
|
-
# service binding for type "ServiceRegistry", so building it throws
|
|
119
|
-
# "Unknown service" inside the host's service loop, and every service
|
|
120
|
-
# registered after it is never built: invoking one then fails with
|
|
121
|
-
# "Timeout waiting for invoke method listener".
|
|
122
|
-
BaseControl.init(self)
|
|
123
|
-
|
|
124
120
|
def register_service(self, service: Service):
|
|
125
121
|
"""
|
|
126
122
|
Registers a service in this registry and pushes an update.
|
|
127
123
|
|
|
124
|
+
If the page hasn't been sent to the client yet, the service is only added
|
|
125
|
+
to the registry and goes out with the page.
|
|
126
|
+
|
|
127
|
+
If the update fails before sending, remove the service and restore the
|
|
128
|
+
subtree's update tracking before re-raising. A failure after sending, such
|
|
129
|
+
as a `did_mount()` exception, leaves the service registered so later patches
|
|
130
|
+
account for the addition already sent.
|
|
131
|
+
|
|
128
132
|
Args:
|
|
129
133
|
service: Service instance to register.
|
|
130
134
|
"""
|
|
@@ -133,7 +137,93 @@ class ServiceRegistry(Service):
|
|
|
133
137
|
f"Registering service {service._c}({service._i}) to registry {self._i}"
|
|
134
138
|
)
|
|
135
139
|
self._services.append(service)
|
|
136
|
-
self.
|
|
140
|
+
if self.parent is None:
|
|
141
|
+
return
|
|
142
|
+
restore = self.__snapshot_update_state()
|
|
143
|
+
try:
|
|
144
|
+
self.__internal_update()
|
|
145
|
+
except BaseException:
|
|
146
|
+
if not self.__was_sent(service):
|
|
147
|
+
for i in range(len(self._services) - 1, -1, -1):
|
|
148
|
+
if self._services[i] is service:
|
|
149
|
+
del self._services[i]
|
|
150
|
+
break
|
|
151
|
+
restore()
|
|
152
|
+
raise
|
|
153
|
+
|
|
154
|
+
def __snapshot_update_state(self) -> Callable[[], None]:
|
|
155
|
+
"""Save diff bookkeeping without copying controls or application state.
|
|
156
|
+
|
|
157
|
+
Both current fields and previous snapshots can contain controls: a child
|
|
158
|
+
pending removal is only reachable through the previous snapshot. Restore
|
|
159
|
+
missing attributes too, since configuring new children attaches parents
|
|
160
|
+
and creates snapshots before serialization can fail.
|
|
161
|
+
"""
|
|
162
|
+
attributes = (
|
|
163
|
+
"_dirty",
|
|
164
|
+
"__changes",
|
|
165
|
+
"__prev_lists",
|
|
166
|
+
"__prev_dicts",
|
|
167
|
+
"__prev_classes",
|
|
168
|
+
"_parent",
|
|
169
|
+
"_initialized",
|
|
170
|
+
"_frozen",
|
|
171
|
+
)
|
|
172
|
+
saved = []
|
|
173
|
+
visited = set()
|
|
174
|
+
|
|
175
|
+
def capture(value):
|
|
176
|
+
if id(value) in visited:
|
|
177
|
+
return
|
|
178
|
+
visited.add(id(value))
|
|
179
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
180
|
+
state = {}
|
|
181
|
+
for name in attributes:
|
|
182
|
+
if not hasattr(value, name):
|
|
183
|
+
continue
|
|
184
|
+
attr = getattr(value, name)
|
|
185
|
+
if isinstance(attr, dict):
|
|
186
|
+
attr = attr.copy()
|
|
187
|
+
if name in ("__prev_lists", "__prev_dicts"):
|
|
188
|
+
attr = {k: v.copy() for k, v in attr.items()}
|
|
189
|
+
state[name] = attr
|
|
190
|
+
saved.append((value, state))
|
|
191
|
+
for name in ("__prev_lists", "__prev_dicts", "__prev_classes"):
|
|
192
|
+
capture(state.get(name))
|
|
193
|
+
for f in fields(value):
|
|
194
|
+
if not f.metadata.get("skip", False):
|
|
195
|
+
capture(getattr(value, f.name))
|
|
196
|
+
elif isinstance(value, dict):
|
|
197
|
+
for item in value.values():
|
|
198
|
+
capture(item)
|
|
199
|
+
elif isinstance(value, (list, tuple)):
|
|
200
|
+
for item in value:
|
|
201
|
+
capture(item)
|
|
202
|
+
|
|
203
|
+
capture(self)
|
|
204
|
+
|
|
205
|
+
def restore():
|
|
206
|
+
for value, state in saved:
|
|
207
|
+
for name in attributes:
|
|
208
|
+
if name in state:
|
|
209
|
+
object.__setattr__(value, name, state[name])
|
|
210
|
+
elif hasattr(value, name):
|
|
211
|
+
object.__delattr__(value, name)
|
|
212
|
+
|
|
213
|
+
return restore
|
|
214
|
+
|
|
215
|
+
def __was_sent(self, service: Service) -> bool:
|
|
216
|
+
"""
|
|
217
|
+
Returns whether the patch adding `service` was sent to the client.
|
|
218
|
+
|
|
219
|
+
The session indexes all additions after submitting the patch to the
|
|
220
|
+
connection, before calling `did_mount()`. This remains true if another
|
|
221
|
+
control's `did_mount()` raises; it does not confirm delivery to the client.
|
|
222
|
+
"""
|
|
223
|
+
try:
|
|
224
|
+
return self.page.session.index.get(service._i) is service
|
|
225
|
+
except RuntimeError:
|
|
226
|
+
return False
|
|
137
227
|
|
|
138
228
|
def unregister_services(self):
|
|
139
229
|
"""
|
|
@@ -165,9 +255,11 @@ class ServiceRegistry(Service):
|
|
|
165
255
|
based solely on whether the user called `.update()` themselves.
|
|
166
256
|
"""
|
|
167
257
|
was_called = context.was_update_called()
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
258
|
+
try:
|
|
259
|
+
self.update()
|
|
260
|
+
finally:
|
|
261
|
+
if not was_called:
|
|
262
|
+
context.reset_update_called()
|
|
171
263
|
|
|
172
264
|
|
|
173
265
|
@dataclass
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, ClassVar, Optional
|
|
3
|
+
|
|
4
|
+
from flet.controls.base_control import BaseControl
|
|
5
|
+
from flet.controls.context import context
|
|
6
|
+
from flet.controls.ref import Ref
|
|
7
|
+
|
|
8
|
+
__all__ = ["Service"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(kw_only=True)
|
|
12
|
+
class Service(BaseControl):
|
|
13
|
+
"""
|
|
14
|
+
Base class for user services.
|
|
15
|
+
|
|
16
|
+
By default, construction registers the service with the current page after
|
|
17
|
+
`init()` returns, whether or not the override calls `super().init()`. Set client
|
|
18
|
+
properties and event handlers in `init()` so they are included in registration
|
|
19
|
+
and available when the Dart service initializes. Without a current page context,
|
|
20
|
+
the service is constructed but not registered.
|
|
21
|
+
|
|
22
|
+
Note:
|
|
23
|
+
Inside `init()`, the service isn't attached to the page yet: `self.page`
|
|
24
|
+
and `self.update()` raise. Use `ft.context.page` to access the current page.
|
|
25
|
+
|
|
26
|
+
Declared fields participate in serialization unless excluded with
|
|
27
|
+
`metadata={"skip": True}`; a leading underscore does not exclude a field.
|
|
28
|
+
Keep Python-only state, such as clients or handles, in excluded fields or
|
|
29
|
+
attributes assigned in `init()` without declaring them as fields.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
_auto_register: ClassVar[bool] = True
|
|
33
|
+
"""
|
|
34
|
+
Whether constructing an instance registers it with the current page.
|
|
35
|
+
|
|
36
|
+
Set to `False` as a class attribute to opt out.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __post_init__(self, ref: Optional[Ref[Any]]):
|
|
40
|
+
"""
|
|
41
|
+
Finalize the service and register it with the current page.
|
|
42
|
+
|
|
43
|
+
Prefer overriding `init()` for setup. If this method is overridden,
|
|
44
|
+
registration occurs inside `super().__post_init__(ref)`: fields assigned
|
|
45
|
+
after that call require a later update to reach the client.
|
|
46
|
+
"""
|
|
47
|
+
super().__post_init__(ref)
|
|
48
|
+
if not self._auto_register:
|
|
49
|
+
return
|
|
50
|
+
try:
|
|
51
|
+
page = context.page
|
|
52
|
+
except RuntimeError:
|
|
53
|
+
return
|
|
54
|
+
page._services.register_service(self)
|
|
@@ -248,6 +248,9 @@ class Session:
|
|
|
248
248
|
and then applied to the local session index by unmounting removed controls and
|
|
249
249
|
mounting added controls.
|
|
250
250
|
|
|
251
|
+
Every added control is indexed before any of them runs `did_mount()`, so the
|
|
252
|
+
index includes all additions in the submitted patch even if a callback raises.
|
|
253
|
+
|
|
251
254
|
Args:
|
|
252
255
|
control: Current control state to patch from.
|
|
253
256
|
prev_control: Previous control snapshot. If `None`, `control` is used.
|
|
@@ -267,12 +270,6 @@ class Session:
|
|
|
267
270
|
for c in removed_controls:
|
|
268
271
|
patch_logger.debug(" %s", c)
|
|
269
272
|
|
|
270
|
-
added_ids = {added_control._i for added_control in added_controls}
|
|
271
|
-
for removed_control in removed_controls:
|
|
272
|
-
if removed_control._i not in added_ids:
|
|
273
|
-
removed_control.will_unmount()
|
|
274
|
-
self.__index.pop(removed_control._i, None)
|
|
275
|
-
|
|
276
273
|
if len(patch) > 1:
|
|
277
274
|
self.__send_message(
|
|
278
275
|
Message(
|
|
@@ -285,9 +282,21 @@ class Session:
|
|
|
285
282
|
for ac in added_controls:
|
|
286
283
|
patch_logger.debug(" %s", ac)
|
|
287
284
|
|
|
288
|
-
|
|
285
|
+
# Commit the entire index before lifecycle callbacks. A failed send must
|
|
286
|
+
# neither unmount existing controls nor remove them from the index, and
|
|
287
|
+
# a failed callback must not roll back an already-submitted service.
|
|
288
|
+
for removed_control in removed_controls:
|
|
289
|
+
self.__index.pop(removed_control._i, None)
|
|
289
290
|
for added_control in added_controls:
|
|
290
291
|
self.__index[added_control._i] = added_control
|
|
292
|
+
|
|
293
|
+
added_ids = {added_control._i for added_control in added_controls}
|
|
294
|
+
for removed_control in removed_controls:
|
|
295
|
+
if removed_control._i not in added_ids:
|
|
296
|
+
removed_control.will_unmount()
|
|
297
|
+
|
|
298
|
+
removed_ids = {removed_control._i for removed_control in removed_controls}
|
|
299
|
+
for added_control in added_controls:
|
|
291
300
|
if added_control._i not in removed_ids:
|
|
292
301
|
added_control.did_mount()
|
|
293
302
|
|
|
@@ -481,13 +481,20 @@ class FletTestApp:
|
|
|
481
481
|
Wraps provided controls in a Screenshot control.
|
|
482
482
|
"""
|
|
483
483
|
controls = list(self.page.controls)
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
484
|
+
# Controls that expand need a bounded host to expand into: shrink-wrapping
|
|
485
|
+
# them (intrinsic width, scrollable height) is flex-in-unbounded and fails
|
|
486
|
+
# layout. Give those the whole page instead.
|
|
487
|
+
expand = True if any(c.expand for c in controls) else None
|
|
488
|
+
scr = ft.Screenshot(
|
|
489
|
+
ft.Column(
|
|
490
|
+
controls,
|
|
491
|
+
margin=margin,
|
|
492
|
+
intrinsic_width=expand is None,
|
|
493
|
+
expand=expand,
|
|
494
|
+
),
|
|
495
|
+
expand=expand,
|
|
496
|
+
)
|
|
497
|
+
self.page.controls = [scr if expand else self.__scrollable_screenshot_host(scr)] # type: ignore
|
|
491
498
|
self.page.update()
|
|
492
499
|
await self.__pump_and_settle_with_timeout("wrap_page_controls_in_screenshot")
|
|
493
500
|
for _ in range(0, pump_times):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Flet for Python - easily build interactive multi-platform apps in Python
|
|
5
5
|
Author-email: "Appveyor Systems Inc." <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -9,23 +9,23 @@ Project-URL: Repository, https://github.com/flet-dev/flet
|
|
|
9
9
|
Project-URL: Documentation, https://flet.dev/docs/
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist: flet-cli==1.0.
|
|
13
|
-
Requires-Dist: flet-web==1.0.
|
|
12
|
+
Requires-Dist: flet-cli==1.0.1; extra == "cli"
|
|
13
|
+
Requires-Dist: flet-web==1.0.1; extra == "web"
|
|
14
14
|
Requires-Dist: oauthlib>=3.2.2; platform_system != "Emscripten"
|
|
15
15
|
Requires-Dist: httpx>=0.28.1; platform_system != "Emscripten"
|
|
16
16
|
Requires-Dist: repath>=0.9.0
|
|
17
17
|
Requires-Dist: msgpack>=1.1.0
|
|
18
18
|
Requires-Dist: typing-extensions; python_version < "3.11"
|
|
19
19
|
Provides-Extra: all
|
|
20
|
-
Requires-Dist: flet-cli==1.0.
|
|
21
|
-
Requires-Dist: flet-web==1.0.
|
|
22
|
-
Requires-Dist: flet-desktop==1.0.
|
|
20
|
+
Requires-Dist: flet-cli==1.0.1; extra == "all"
|
|
21
|
+
Requires-Dist: flet-web==1.0.1; extra == "all"
|
|
22
|
+
Requires-Dist: flet-desktop==1.0.1; extra == "all"
|
|
23
23
|
Provides-Extra: cli
|
|
24
|
-
Requires-Dist: flet-cli==1.0.
|
|
24
|
+
Requires-Dist: flet-cli==1.0.1; extra == "cli"
|
|
25
25
|
Provides-Extra: desktop
|
|
26
|
-
Requires-Dist: flet-desktop==1.0.
|
|
26
|
+
Requires-Dist: flet-desktop==1.0.1; extra == "desktop"
|
|
27
27
|
Provides-Extra: web
|
|
28
|
-
Requires-Dist: flet-web==1.0.
|
|
28
|
+
Requires-Dist: flet-web==1.0.1; extra == "web"
|
|
29
29
|
Provides-Extra: test
|
|
30
30
|
Requires-Dist: pytest>=7.2.0; extra == "test"
|
|
31
31
|
Requires-Dist: pytest-asyncio>=1.1.0; extra == "test"
|
|
@@ -41,7 +41,7 @@ Requires-Dist: scikit-image>=0.25.2; extra == "test"
|
|
|
41
41
|
</p>
|
|
42
42
|
|
|
43
43
|
<p align="center">
|
|
44
|
-
<em>Build
|
|
44
|
+
<em>Build cross-platform apps in Python. No frontend experience required.</em>
|
|
45
45
|
</p>
|
|
46
46
|
|
|
47
47
|
<p align="center">
|
|
@@ -59,10 +59,11 @@ Requires-Dist: scikit-image>=0.25.2; extra == "test"
|
|
|
59
59
|
<img src="https://flet.dev/docs/assets/badges/docs-coverage/flet.svg" alt="Docstring coverage" /></a>
|
|
60
60
|
</p>
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Flet is a Python framework for building web, desktop, and mobile apps without prior experience in frontend development.
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
> **[Try Flet online in Flet Studio](https://studio.flet.dev)**
|
|
65
|
+
>
|
|
66
|
+
> Create, run, and share Python apps in your browser. Start from an example, write your own code, or get help from the AI agent. No installation required.
|
|
66
67
|
|
|
67
68
|
### <img src="https://flet.dev/img/pages/home/single-code-base.svg" width="25" align="top" /> Single code base for any device
|
|
68
69
|
|