flet 0.86.0.dev2__tar.gz → 0.86.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.

Potentially problematic release.


This version of flet might be problematic. Click here for more details.

Files changed (321) hide show
  1. {flet-0.86.0.dev2 → flet-0.86.1}/PKG-INFO +9 -9
  2. {flet-0.86.0.dev2 → flet-0.86.1}/pyproject.toml +9 -9
  3. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/__init__.py +3 -0
  4. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/base_control.py +1 -2
  5. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/control_event.py +1 -2
  6. flet-0.86.1/src/flet/controls/core/raw_image.py +475 -0
  7. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/types.py +11 -9
  8. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/messaging/connection.py +5 -0
  9. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/messaging/flet_dart_bridge_server.py +2 -0
  10. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/messaging/flet_socket_server.py +12 -3
  11. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/messaging/pyodide_connection.py +2 -0
  12. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/version.py +1 -1
  13. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet.egg-info/PKG-INFO +9 -9
  14. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet.egg-info/SOURCES.txt +2 -0
  15. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet.egg-info/requires.txt +8 -8
  16. flet-0.86.1/tests/test_raw_image_pixels.py +133 -0
  17. {flet-0.86.0.dev2 → flet-0.86.1}/README.md +0 -0
  18. {flet-0.86.0.dev2 → flet-0.86.1}/setup.cfg +0 -0
  19. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/app.py +0 -0
  20. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/__init__.py +0 -0
  21. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/authorization.py +0 -0
  22. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/authorization_service.py +0 -0
  23. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/group.py +0 -0
  24. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/oauth_provider.py +0 -0
  25. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/oauth_token.py +0 -0
  26. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/providers/__init__.py +0 -0
  27. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/providers/auth0_oauth_provider.py +0 -0
  28. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/providers/azure_oauth_provider.py +0 -0
  29. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/providers/github_oauth_provider.py +0 -0
  30. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/providers/google_oauth_provider.py +0 -0
  31. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/auth/user.py +0 -0
  32. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/canvas/__init__.py +0 -0
  33. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/cli.py +0 -0
  34. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/__init__.py +0 -0
  35. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/component.py +0 -0
  36. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/component_decorator.py +0 -0
  37. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/component_owned.py +0 -0
  38. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/__init__.py +0 -0
  39. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/hook.py +0 -0
  40. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/use_callback.py +0 -0
  41. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/use_context.py +0 -0
  42. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/use_dialog.py +0 -0
  43. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/use_effect.py +0 -0
  44. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/use_memo.py +0 -0
  45. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/use_ref.py +0 -0
  46. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/hooks/use_state.py +0 -0
  47. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/memo.py +0 -0
  48. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/observable.py +0 -0
  49. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/public_utils.py +0 -0
  50. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/router.py +0 -0
  51. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/components/utils.py +0 -0
  52. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/__init__.py +0 -0
  53. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/adaptive_control.py +0 -0
  54. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/alignment.py +0 -0
  55. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/animation.py +0 -0
  56. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/base_page.py +0 -0
  57. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/blur.py +0 -0
  58. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/border.py +0 -0
  59. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/border_radius.py +0 -0
  60. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/box.py +0 -0
  61. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/buttons.py +0 -0
  62. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/colors.py +0 -0
  63. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/context.py +0 -0
  64. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/control.py +0 -0
  65. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/control_state.py +0 -0
  66. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/__init__.py +0 -0
  67. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/animated_switcher.py +0 -0
  68. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/autofill_group.py +0 -0
  69. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/__init__.py +0 -0
  70. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/arc.py +0 -0
  71. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/canvas.py +0 -0
  72. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/circle.py +0 -0
  73. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/color.py +0 -0
  74. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/fill.py +0 -0
  75. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/image.py +0 -0
  76. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/line.py +0 -0
  77. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/oval.py +0 -0
  78. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/path.py +0 -0
  79. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/points.py +0 -0
  80. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/rect.py +0 -0
  81. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/shadow.py +0 -0
  82. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/shape.py +0 -0
  83. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/canvas/text.py +0 -0
  84. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/column.py +0 -0
  85. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/dismissible.py +0 -0
  86. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/drag_target.py +0 -0
  87. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/draggable.py +0 -0
  88. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/flet_app.py +0 -0
  89. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/gesture_detector.py +0 -0
  90. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/grid_view.py +0 -0
  91. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/hero.py +0 -0
  92. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/icon.py +0 -0
  93. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/image.py +0 -0
  94. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/interactive_viewer.py +0 -0
  95. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/keyboard_listener.py +0 -0
  96. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/list_view.py +0 -0
  97. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/markdown.py +0 -0
  98. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/merge_semantics.py +0 -0
  99. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/page_view.py +0 -0
  100. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/pagelet.py +0 -0
  101. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/placeholder.py +0 -0
  102. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/reorderable_drag_handle.py +0 -0
  103. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/responsive_row.py +0 -0
  104. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/rotated_box.py +0 -0
  105. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/row.py +0 -0
  106. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/safe_area.py +0 -0
  107. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/screenshot.py +0 -0
  108. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/semantics.py +0 -0
  109. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/shader_mask.py +0 -0
  110. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/shimmer.py +0 -0
  111. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/stack.py +0 -0
  112. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/text.py +0 -0
  113. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/text_span.py +0 -0
  114. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/transparent_pointer.py +0 -0
  115. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/view.py +0 -0
  116. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/window.py +0 -0
  117. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/core/window_drag_area.py +0 -0
  118. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/__init__.py +0 -0
  119. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_action_sheet.py +0 -0
  120. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_action_sheet_action.py +0 -0
  121. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_activity_indicator.py +0 -0
  122. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_alert_dialog.py +0 -0
  123. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_app_bar.py +0 -0
  124. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_bottom_sheet.py +0 -0
  125. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_button.py +0 -0
  126. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_checkbox.py +0 -0
  127. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_colors.py +0 -0
  128. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_context_menu.py +0 -0
  129. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_context_menu_action.py +0 -0
  130. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_date_picker.py +0 -0
  131. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_dialog_action.py +0 -0
  132. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_filled_button.py +0 -0
  133. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_icons.json +0 -0
  134. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_icons.py +0 -0
  135. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_icons.pyi +0 -0
  136. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_list_tile.py +0 -0
  137. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_navigation_bar.py +0 -0
  138. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_picker.py +0 -0
  139. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_radio.py +0 -0
  140. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_segmented_button.py +0 -0
  141. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_slider.py +0 -0
  142. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_sliding_segmented_button.py +0 -0
  143. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_switch.py +0 -0
  144. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_textfield.py +0 -0
  145. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_timer_picker.py +0 -0
  146. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/cupertino/cupertino_tinted_button.py +0 -0
  147. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/device_info.py +0 -0
  148. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/dialog_control.py +0 -0
  149. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/duration.py +0 -0
  150. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/embed_json_encoder.py +0 -0
  151. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/events.py +0 -0
  152. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/exceptions.py +0 -0
  153. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/geometry.py +0 -0
  154. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/gradients.py +0 -0
  155. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/icon_data.py +0 -0
  156. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/id_counter.py +0 -0
  157. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/keys.py +0 -0
  158. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/layout_control.py +0 -0
  159. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/margin.py +0 -0
  160. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/__init__.py +0 -0
  161. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/alert_dialog.py +0 -0
  162. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/app_bar.py +0 -0
  163. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/auto_complete.py +0 -0
  164. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/badge.py +0 -0
  165. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/banner.py +0 -0
  166. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/bottom_app_bar.py +0 -0
  167. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/bottom_sheet.py +0 -0
  168. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/button.py +0 -0
  169. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/card.py +0 -0
  170. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/checkbox.py +0 -0
  171. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/chip.py +0 -0
  172. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/circle_avatar.py +0 -0
  173. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/container.py +0 -0
  174. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/context_menu.py +0 -0
  175. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/datatable.py +0 -0
  176. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/date_picker.py +0 -0
  177. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/date_range_picker.py +0 -0
  178. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/divider.py +0 -0
  179. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/dropdown.py +0 -0
  180. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/dropdownm2.py +0 -0
  181. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/elevated_button.py +0 -0
  182. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/expansion_panel.py +0 -0
  183. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/expansion_tile.py +0 -0
  184. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/filled_button.py +0 -0
  185. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/filled_tonal_button.py +0 -0
  186. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/floating_action_button.py +0 -0
  187. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/form_field_control.py +0 -0
  188. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/icon_button.py +0 -0
  189. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/icons.json +0 -0
  190. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/icons.py +0 -0
  191. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/icons.pyi +0 -0
  192. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/list_tile.py +0 -0
  193. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/menu_bar.py +0 -0
  194. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/menu_item_button.py +0 -0
  195. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/navigation_bar.py +0 -0
  196. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/navigation_drawer.py +0 -0
  197. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/navigation_rail.py +0 -0
  198. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/outlined_button.py +0 -0
  199. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/popup_menu_button.py +0 -0
  200. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/progress_bar.py +0 -0
  201. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/progress_ring.py +0 -0
  202. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/radio.py +0 -0
  203. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/radio_group.py +0 -0
  204. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/range_slider.py +0 -0
  205. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/reorderable_list_view.py +0 -0
  206. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/search_bar.py +0 -0
  207. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/segmented_button.py +0 -0
  208. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/selection_area.py +0 -0
  209. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/slider.py +0 -0
  210. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/snack_bar.py +0 -0
  211. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/submenu_button.py +0 -0
  212. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/switch.py +0 -0
  213. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/tabs.py +0 -0
  214. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/text_button.py +0 -0
  215. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/textfield.py +0 -0
  216. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/time_picker.py +0 -0
  217. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/tooltip.py +0 -0
  218. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/material/vertical_divider.py +0 -0
  219. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/multi_view.py +0 -0
  220. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/object_patch.py +0 -0
  221. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/padding.py +0 -0
  222. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/page.py +0 -0
  223. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/painting.py +0 -0
  224. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/query_string.py +0 -0
  225. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/ref.py +0 -0
  226. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/scrollable_control.py +0 -0
  227. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/__init__.py +0 -0
  228. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/accelerometer.py +0 -0
  229. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/barometer.py +0 -0
  230. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/battery.py +0 -0
  231. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/browser_context_menu.py +0 -0
  232. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/clipboard.py +0 -0
  233. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/connectivity.py +0 -0
  234. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/file_picker.py +0 -0
  235. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/gyroscope.py +0 -0
  236. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/haptic_feedback.py +0 -0
  237. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/magnetometer.py +0 -0
  238. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/screen_brightness.py +0 -0
  239. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/semantics_service.py +0 -0
  240. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/sensor_error_event.py +0 -0
  241. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/service.py +0 -0
  242. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/shake_detector.py +0 -0
  243. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/share.py +0 -0
  244. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/shared_preferences.py +0 -0
  245. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/storage_paths.py +0 -0
  246. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/url_launcher.py +0 -0
  247. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/user_accelerometer.py +0 -0
  248. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/services/wakelock.py +0 -0
  249. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/template_route.py +0 -0
  250. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/text_style.py +0 -0
  251. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/theme.py +0 -0
  252. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/transform.py +0 -0
  253. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/controls/value_types.py +0 -0
  254. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/data_channel.py +0 -0
  255. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/fastapi/__init__.py +0 -0
  256. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/messaging/protocol.py +0 -0
  257. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/messaging/session.py +0 -0
  258. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/messaging/session_store.py +0 -0
  259. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/pubsub/__init__.py +0 -0
  260. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/pubsub/pubsub_client.py +0 -0
  261. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/pubsub/pubsub_hub.py +0 -0
  262. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/py.typed +0 -0
  263. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/pytest_plugin.py +0 -0
  264. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/security/__init__.py +0 -0
  265. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/testing/__init__.py +0 -0
  266. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/testing/finder.py +0 -0
  267. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/testing/flet_test_app.py +0 -0
  268. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/testing/remote_tester.py +0 -0
  269. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/testing/tester.py +0 -0
  270. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/__init__.py +0 -0
  271. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/browser.py +0 -0
  272. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/classproperty.py +0 -0
  273. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/deprecated.py +0 -0
  274. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/deprecated_enum.py +0 -0
  275. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/files.py +0 -0
  276. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/from_dict.py +0 -0
  277. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/griffe_deprecations.py +0 -0
  278. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/hashing.py +0 -0
  279. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/json_utils.py +0 -0
  280. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/locks.py +0 -0
  281. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/network.py +0 -0
  282. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/object_model.py +0 -0
  283. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/once.py +0 -0
  284. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/pip.py +0 -0
  285. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/platform_utils.py +0 -0
  286. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/slugify.py +0 -0
  287. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/strings.py +0 -0
  288. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/typing_utils.py +0 -0
  289. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/validation.py +0 -0
  290. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet/utils/vector.py +0 -0
  291. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet.egg-info/dependency_links.txt +0 -0
  292. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet.egg-info/entry_points.txt +0 -0
  293. {flet-0.86.0.dev2 → flet-0.86.1}/src/flet.egg-info/top_level.txt +0 -0
  294. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_auth_lazy_imports.py +0 -0
  295. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_base_control.py +0 -0
  296. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_colors.py +0 -0
  297. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_component_diff.py +0 -0
  298. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_component_effects.py +0 -0
  299. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_component_renderer.py +0 -0
  300. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_dataclasses.py +0 -0
  301. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_datetime.py +0 -0
  302. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_deprecated.py +0 -0
  303. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_events.py +0 -0
  304. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_from_dict.py +0 -0
  305. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_griffe_deprecations.py +0 -0
  306. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_icons.py +0 -0
  307. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_object_diff_frozen.py +0 -0
  308. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_object_diff_in_place.py +0 -0
  309. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_object_diff_memory_churn.py +0 -0
  310. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_object_diff_performance.py +0 -0
  311. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_observable.py +0 -0
  312. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_patch_dataclass.py +0 -0
  313. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_router_modal.py +0 -0
  314. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_router_pop.py +0 -0
  315. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_router_recursive.py +0 -0
  316. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_session_disconnect_buffering.py +0 -0
  317. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_skip_double_update.py +0 -0
  318. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_use_dialog.py +0 -0
  319. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_validation.py +0 -0
  320. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_validation_benchmark.py +0 -0
  321. {flet-0.86.0.dev2 → flet-0.86.1}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet
3
- Version: 0.86.0.dev2
3
+ Version: 0.86.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==0.86.0.dev2; extra == "cli"
13
- Requires-Dist: flet-web==0.86.0.dev2; extra == "web"
12
+ Requires-Dist: flet-cli==0.86.1; extra == "cli"
13
+ Requires-Dist: flet-web==0.86.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==0.86.0.dev2; extra == "all"
21
- Requires-Dist: flet-web==0.86.0.dev2; extra == "all"
22
- Requires-Dist: flet-desktop==0.86.0.dev2; extra == "all"
20
+ Requires-Dist: flet-cli==0.86.1; extra == "all"
21
+ Requires-Dist: flet-web==0.86.1; extra == "all"
22
+ Requires-Dist: flet-desktop==0.86.1; extra == "all"
23
23
  Provides-Extra: cli
24
- Requires-Dist: flet-cli==0.86.0.dev2; extra == "cli"
24
+ Requires-Dist: flet-cli==0.86.1; extra == "cli"
25
25
  Provides-Extra: desktop
26
- Requires-Dist: flet-desktop==0.86.0.dev2; extra == "desktop"
26
+ Requires-Dist: flet-desktop==0.86.1; extra == "desktop"
27
27
  Provides-Extra: web
28
- Requires-Dist: flet-web==0.86.0.dev2; extra == "web"
28
+ Requires-Dist: flet-web==0.86.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"
@@ -1,14 +1,14 @@
1
1
  [project]
2
2
  name = "flet"
3
- version = "0.86.0.dev2"
3
+ version = "0.86.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==0.86.0.dev2; extra == 'cli'",
11
- "flet-web==0.86.0.dev2; extra == 'web'",
10
+ "flet-cli==0.86.1; extra == 'cli'",
11
+ "flet-web==0.86.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==0.86.0.dev2",
27
- "flet-web==0.86.0.dev2",
28
- "flet-desktop==0.86.0.dev2",
26
+ "flet-cli==0.86.1",
27
+ "flet-web==0.86.1",
28
+ "flet-desktop==0.86.1",
29
29
  ]
30
- cli = ["flet-cli==0.86.0.dev2"]
30
+ cli = ["flet-cli==0.86.1"]
31
31
  desktop = [
32
- "flet-desktop==0.86.0.dev2",
32
+ "flet-desktop==0.86.1",
33
33
  ]
34
- web = ["flet-web==0.86.0.dev2"]
34
+ web = ["flet-web==0.86.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.
@@ -183,6 +183,7 @@ if TYPE_CHECKING:
183
183
  from flet.controls.core.page_view import PageView
184
184
  from flet.controls.core.pagelet import Pagelet
185
185
  from flet.controls.core.placeholder import Placeholder
186
+ from flet.controls.core.raw_image import RawImage
186
187
  from flet.controls.core.reorderable_drag_handle import ReorderableDragHandle
187
188
  from flet.controls.core.responsive_row import ResponsiveRow
188
189
  from flet.controls.core.rotated_box import RotatedBox
@@ -1070,6 +1071,7 @@ __all__ = [
1070
1071
  "RadioGroup",
1071
1072
  "RadioTheme",
1072
1073
  "RangeSlider",
1074
+ "RawImage",
1073
1075
  "Rect",
1074
1076
  "Ref",
1075
1077
  "ReorderableDragHandle",
@@ -1603,6 +1605,7 @@ _LAZY = {
1603
1605
  "RadioGroup": "flet.controls.material.radio_group",
1604
1606
  "RadioTheme": "flet.controls.theme",
1605
1607
  "RangeSlider": "flet.controls.material.range_slider",
1608
+ "RawImage": "flet.controls.core.raw_image",
1606
1609
  "Rect": "flet.controls.geometry",
1607
1610
  "Ref": "flet.controls.ref",
1608
1611
  "ReorderableDragHandle": "flet.controls.core.reorderable_drag_handle",
@@ -23,7 +23,6 @@ else:
23
23
 
24
24
 
25
25
  if TYPE_CHECKING:
26
- from .base_page import BasePage
27
26
  from .keys import KeyValue
28
27
  from .page import Page
29
28
 
@@ -296,7 +295,7 @@ class BaseControl:
296
295
  return parent_ref() if parent_ref else None
297
296
 
298
297
  @property
299
- def page(self) -> "Union[Page, BasePage]":
298
+ def page(self) -> "Page":
300
299
  """
301
300
  The page to which this control belongs to.
302
301
  """
@@ -19,7 +19,6 @@ from flet.utils.typing_utils import eval_type
19
19
  if TYPE_CHECKING:
20
20
  from .base_control import BaseControl # noqa
21
21
  from .page import Page
22
- from .base_page import BasePage
23
22
 
24
23
  _BaseControlType = BaseControl
25
24
  else:
@@ -128,7 +127,7 @@ class Event(Generic[EventControlType]):
128
127
  """
129
128
 
130
129
  @property
131
- def page(self) -> Union["Page", "BasePage"]:
130
+ def page(self) -> "Page":
132
131
  """
133
132
  Page that owns the event source control.
134
133
  """
@@ -0,0 +1,475 @@
1
+ import asyncio
2
+ import contextlib
3
+ import io
4
+ from collections import deque
5
+ from typing import Any, Optional
6
+
7
+ from flet.controls.base_control import control
8
+ from flet.controls.box import BoxFit, FilterQuality
9
+ from flet.controls.control_event import EventHandler
10
+ from flet.controls.layout_control import LayoutControl
11
+ from flet.controls.types import Number
12
+ from flet.data_channel import DataChannel, DataChannelOpenEvent
13
+
14
+ __all__ = ["RawImage"]
15
+
16
+
17
+ @control("RawImage")
18
+ class RawImage(LayoutControl):
19
+ """
20
+ Displays pixel frames pushed from Python at full bandwidth.
21
+
22
+ Unlike :class:`~flet.Image`, whose `src` travels through the regular
23
+ Flet protocol on every update, `RawImage` streams frames over a
24
+ dedicated :class:`~flet.DataChannel`: bytes skip MsgPack
25
+ encode/decode entirely and, on local transports (desktop app, `flet run`,
26
+ Pyodide), are displayed from raw RGBA pixels without any image
27
+ encoding or decoding. This makes it suitable for animations, generated
28
+ graphics, camera frames and Pillow output at interactive frame rates.
29
+
30
+ Frames are pushed with the awaitable :meth:`render`, :meth:`render_rgba`
31
+ and :meth:`render_encoded` methods. Each call resolves when the client
32
+ has actually displayed the frame, so a plain loop self-paces to display
33
+ speed:
34
+
35
+ ```python
36
+ raw_image = ft.RawImage(width=400, height=300)
37
+ page.add(raw_image)
38
+
39
+ while True:
40
+ await raw_image.render(produce_pil_image())
41
+ ```
42
+
43
+ Transport behavior is selected automatically:
44
+
45
+ - **Local transports** (`local_data_transport` connections): frames are
46
+ sent as uncompressed premultiplied RGBA8888 and uploaded straight to
47
+ a GPU texture on the client.
48
+ - **Remote transports** (`flet-web` over WebSocket): Pillow and NumPy
49
+ frames are PNG-encoded off the event loop to save bandwidth; raw
50
+ pixel frames for which no straight-alpha source is available are sent
51
+ uncompressed.
52
+
53
+ **Premultiplied alpha.** The raw-pixel path uploads frames directly as
54
+ GPU textures in Flutter's `rgba8888` format, which expects RGB values
55
+ already multiplied by alpha ("premultiplied"). Pillow, numpy code and
56
+ image files normally produce *straight* alpha instead, where color and
57
+ opacity are independent: a half-transparent white pixel is
58
+ `(255, 255, 255, 128)` straight but `(128, 128, 128, 128)`
59
+ premultiplied (each color channel becomes `value * alpha // 255`).
60
+ The `premultiplied` argument of the render methods says whether that
61
+ conversion is already done:
62
+
63
+ - `premultiplied=False` — pixels carry straight alpha; RGB is
64
+ multiplied by alpha before sending. The conversion runs in fast C
65
+ loops and is skipped when the frame turns out to be fully opaque.
66
+ - `premultiplied=True` — skip the conversion. Use it when pixels are
67
+ genuinely premultiplied or, the common case, when the frame is
68
+ **fully opaque**: with every alpha at 255 both forms are identical,
69
+ and declaring it saves an alpha scan per frame in streaming loops.
70
+
71
+ Passing straight-alpha pixels with `premultiplied=True` makes
72
+ semi-transparent areas render too bright; for fully opaque frames the
73
+ flag cannot be wrong either way.
74
+
75
+ The last frame is retained and replayed automatically when the client
76
+ widget remounts (page rebuild, route navigation), mirroring how
77
+ `Image.src` persists.
78
+ """
79
+
80
+ fit: Optional[BoxFit] = None
81
+ """
82
+ Defines how to inscribe the current frame into the space allocated
83
+ during layout.
84
+ """
85
+
86
+ filter_quality: FilterQuality = FilterQuality.LOW
87
+ """
88
+ The rendering quality of the displayed frame.
89
+
90
+ Defaults to `LOW` (bilinear), a good trade-off for streamed frames.
91
+ Use `NONE` for crisp nearest-neighbor scaling of pixel-art-style
92
+ content.
93
+ """
94
+
95
+ scale: Number = 1.0
96
+ """
97
+ How many physical frame pixels correspond to one logical pixel.
98
+
99
+ Only affects the intrinsic size of this control when it is laid out
100
+ without tight constraints: a 800x600 frame with `scale=2` measures
101
+ 400x300 logical pixels. Set it to the device pixel ratio when frames
102
+ are rendered at physical resolution.
103
+ """
104
+
105
+ ready_timeout: Optional[Number] = 5.0
106
+ """
107
+ Seconds the `render` methods wait for the client widget to attach its
108
+ data channel before raising `TimeoutError`.
109
+
110
+ Set to `None` to wait indefinitely.
111
+ """
112
+
113
+ ack_timeout: Optional[Number] = 10.0
114
+ """
115
+ Seconds a `render` call waits for the client's frame-applied
116
+ acknowledgment before raising `TimeoutError`.
117
+
118
+ The ack normally arrives within milliseconds of the frame being
119
+ displayed. The timeout is a liveness guard: transports drop frames
120
+ sent while the client is disconnected (closed browser tab, network
121
+ loss), and without it a `while True: await raw_image.render(...)`
122
+ loop would wait for the lost ack forever. Raise it when streaming
123
+ very large frames to slow remote clients, or set to `None` to wait
124
+ indefinitely.
125
+ """
126
+
127
+ on_data_channel_open: Optional[EventHandler[DataChannelOpenEvent]] = None
128
+ """
129
+ Framework hook — Dart fires this when it opens the data channel on
130
+ mount. The default handler captures the channel used by the `render`
131
+ methods; override only to do something extra at attach-time.
132
+ """
133
+
134
+ def init(self) -> None:
135
+ super().init()
136
+ self._channel: Optional[DataChannel] = None
137
+ # Set once the Dart side has announced its data channel; `render`
138
+ # calls block on it so frames pushed before first mount are held
139
+ # (up to `ready_timeout`) instead of dropped.
140
+ self._ready = asyncio.Event()
141
+ # FIFO of per-frame ack futures. Each send enqueues a future and
142
+ # awaits it; `_on_dart_message` resolves the head when Dart's
143
+ # `[0xFF]` frame-applied ack arrives. The await is the
144
+ # backpressure that paces producer loops to display speed.
145
+ self._pending_acks: deque[asyncio.Future] = deque()
146
+ # Last frame packet sent, replayed when the client widget
147
+ # remounts so the image survives page rebuilds like `Image.src`.
148
+ self._last_packet: Optional[bytes] = None
149
+ if self.on_data_channel_open is None:
150
+ self.on_data_channel_open = self._capture_channel
151
+
152
+ # -- channel plumbing ---------------------------------------------------
153
+
154
+ def _capture_channel(self, e: DataChannelOpenEvent) -> None:
155
+ # Single-channel widget; no need to dispatch on e.channel_name.
156
+ self._channel = self.get_data_channel(e.channel_id)
157
+ self._channel.on_bytes(self._on_dart_message)
158
+ # Acks pending on a previous channel will never arrive; resolve
159
+ # them so old `render` awaits return instead of hanging.
160
+ while self._pending_acks:
161
+ fut = self._pending_acks.popleft()
162
+ if not fut.done():
163
+ fut.set_result(None)
164
+ # Replay the last frame without registering an ack future — its
165
+ # ack finds an empty deque and is ignored.
166
+ if self._last_packet is not None:
167
+ self._channel.send(self._last_packet)
168
+ self._ready.set()
169
+
170
+ def _on_dart_message(self, payload: bytes) -> None:
171
+ # Reverse direction (Dart -> Python) wire format:
172
+ # [0xFF] — frame-applied ack, sent after each frame completes.
173
+ if not payload or payload[0] != 0xFF:
174
+ return
175
+ if self._pending_acks:
176
+ fut = self._pending_acks.popleft()
177
+ if not fut.done():
178
+ fut.set_result(None)
179
+
180
+ async def _send_and_wait(self, packet: bytes) -> None:
181
+ if not self._ready.is_set():
182
+ await asyncio.wait_for(
183
+ self._ready.wait(),
184
+ None if self.ready_timeout is None else float(self.ready_timeout),
185
+ )
186
+ assert self._channel is not None
187
+ loop = asyncio.get_running_loop()
188
+ fut: asyncio.Future = loop.create_future()
189
+ self._pending_acks.append(fut)
190
+ self._channel.send(packet)
191
+ if self.ack_timeout is None:
192
+ await fut
193
+ return
194
+ try:
195
+ await asyncio.wait_for(fut, float(self.ack_timeout))
196
+ except (TimeoutError, asyncio.TimeoutError):
197
+ # Withdraw the abandoned future so a future ack doesn't
198
+ # resolve it instead of the frame it belongs to.
199
+ with contextlib.suppress(ValueError):
200
+ self._pending_acks.remove(fut)
201
+ raise TimeoutError(
202
+ f"frame was not acknowledged within {self.ack_timeout}s — "
203
+ "the client may have disconnected"
204
+ ) from None
205
+
206
+ async def _send_frame(self, packet: bytes) -> None:
207
+ self._last_packet = packet
208
+ await self._send_and_wait(packet)
209
+
210
+ def _local_data_transport(self) -> bool:
211
+ try:
212
+ page = self.page
213
+ except RuntimeError: # not added to a page yet
214
+ return False
215
+ return bool(getattr(page.session.connection, "local_data_transport", False))
216
+
217
+ # -- public API ---------------------------------------------------------
218
+
219
+ async def render(self, image: Any, *, premultiplied: bool = False) -> None:
220
+ """
221
+ Display a Pillow image or a NumPy-style array and wait until the
222
+ client has shown it.
223
+
224
+ Args:
225
+ image: A `PIL.Image.Image` (any mode; converted to RGBA as
226
+ needed), or an object exposing `__array_interface__`
227
+ (e.g. a NumPy array) of shape `(height, width, 4)` or
228
+ `(height, width, 3)` with `uint8` values.
229
+ premultiplied: Set to `True` if RGB values are already
230
+ premultiplied by alpha to skip the premultiplication step.
231
+
232
+ Raises:
233
+ TypeError: If `image` is neither a Pillow image nor an
234
+ array-like object.
235
+ TimeoutError: If the client widget doesn't attach within
236
+ :attr:`ready_timeout`.
237
+ """
238
+ if hasattr(image, "mode") and hasattr(image, "tobytes"):
239
+ await self._render_pil(image, premultiplied)
240
+ elif hasattr(image, "__array_interface__"):
241
+ await self._render_array(image, premultiplied)
242
+ else:
243
+ raise TypeError(
244
+ "RawImage.render() accepts a PIL.Image.Image or an object "
245
+ f"with __array_interface__, got {type(image).__name__}"
246
+ )
247
+
248
+ async def render_rgba(
249
+ self,
250
+ width: int,
251
+ height: int,
252
+ pixels: bytes,
253
+ *,
254
+ premultiplied: bool = True,
255
+ ) -> None:
256
+ """
257
+ Display a raw RGBA8888 frame and wait until the client has shown it.
258
+
259
+ Args:
260
+ width: Frame width in physical pixels.
261
+ height: Frame height in physical pixels.
262
+ pixels: Tightly packed RGBA8888 bytes, `width * height * 4`
263
+ long. RGB values must be premultiplied by alpha (fully
264
+ opaque frames are premultiplied by definition).
265
+ premultiplied: Set to `False` if RGB values are straight
266
+ (non-premultiplied); Pillow is then required to
267
+ premultiply them.
268
+
269
+ Raises:
270
+ ValueError: If `pixels` has the wrong length.
271
+ TimeoutError: If the client widget doesn't attach within
272
+ :attr:`ready_timeout`.
273
+ """
274
+ if len(pixels) != width * height * 4:
275
+ raise ValueError(
276
+ f"pixels must be width * height * 4 = {width * height * 4} "
277
+ f"bytes, got {len(pixels)}"
278
+ )
279
+ if not premultiplied and _rgba_is_opaque(pixels):
280
+ premultiplied = True # straight == premultiplied when opaque
281
+ local = self._local_data_transport()
282
+ if not premultiplied:
283
+ # Straight alpha: Pillow is required either to premultiply
284
+ # (local raw upload) or to PNG-encode (remote).
285
+ img = _pil_from_rgba(width, height, pixels)
286
+ if local:
287
+ pixels = _premultiply_pil(img).tobytes()
288
+ else:
289
+ await self._send_frame(
290
+ b"\x01" + await asyncio.to_thread(_pil_to_png_bytes, img)
291
+ )
292
+ return
293
+ elif not local and _rgba_is_opaque(pixels):
294
+ # Opaque frames PNG-encode losslessly (nothing to premultiply);
295
+ # non-opaque premultiplied frames have no straight-alpha source
296
+ # to encode and are sent raw.
297
+ img = _try_pil_from_rgba(width, height, pixels)
298
+ if img is not None:
299
+ await self._send_frame(
300
+ b"\x01" + await asyncio.to_thread(_pil_to_png_bytes, img)
301
+ )
302
+ return
303
+ await self._send_frame(_encode_raw_packet(width, height, pixels))
304
+
305
+ async def render_encoded(self, data: bytes) -> None:
306
+ """
307
+ Display an encoded image (PNG, JPEG, WebP, ...) and wait until the
308
+ client has shown it.
309
+
310
+ The bytes still travel over the data channel (skipping the Flet
311
+ protocol), but the client decodes them with its image codecs
312
+ instead of a raw pixel upload.
313
+
314
+ Args:
315
+ data: Encoded image bytes.
316
+
317
+ Raises:
318
+ TimeoutError: If the client widget doesn't attach within
319
+ :attr:`ready_timeout`.
320
+ """
321
+ await self._send_frame(b"\x01" + data)
322
+
323
+ async def clear(self) -> None:
324
+ """
325
+ Clear the displayed frame.
326
+ """
327
+ self._last_packet = None
328
+ await self._send_and_wait(b"\x03")
329
+
330
+ # -- input paths ----------------------------------------------------------
331
+
332
+ async def _render_pil(self, image: Any, premultiplied: bool) -> None:
333
+ if not self._local_data_transport():
334
+ # PNG carries straight alpha — encode the original image,
335
+ # never a premultiplied copy. Encoding is CPU-heavy; keep it
336
+ # off the event loop.
337
+ await self._send_frame(
338
+ b"\x01" + await asyncio.to_thread(_pil_to_png_bytes, image)
339
+ )
340
+ return
341
+ if image.mode != "RGBA":
342
+ image = image.convert("RGBA")
343
+ if not premultiplied:
344
+ image = _premultiply_pil(image)
345
+ width, height = image.size
346
+ await self._send_frame(_encode_raw_packet(width, height, image.tobytes()))
347
+
348
+ async def _render_array(self, array: Any, premultiplied: bool) -> None:
349
+ # An object with __array_interface__ guarantees numpy is importable
350
+ # in practice; import lazily so flet itself never depends on it.
351
+ import numpy as np
352
+
353
+ arr = np.asarray(array)
354
+ if arr.dtype != np.uint8 or arr.ndim != 3 or arr.shape[2] not in (3, 4):
355
+ raise TypeError(
356
+ "array must be uint8 with shape (height, width, 4) or "
357
+ f"(height, width, 3), got {arr.dtype} {arr.shape}"
358
+ )
359
+ if arr.shape[2] == 3:
360
+ rgba = np.empty(arr.shape[:2] + (4,), dtype=np.uint8)
361
+ rgba[:, :, :3] = arr
362
+ rgba[:, :, 3] = 255
363
+ arr = rgba
364
+ premultiplied = True # opaque
365
+ if not self._local_data_transport():
366
+ try:
367
+ from PIL import Image as PILImage
368
+ except ImportError:
369
+ pass
370
+ else:
371
+ if premultiplied and (arr[:, :, 3] != 255).any():
372
+ # No straight-alpha source to encode — send raw.
373
+ await self._send_frame(self._array_packet(np, arr, True))
374
+ return
375
+ img = PILImage.frombuffer(
376
+ "RGBA",
377
+ (arr.shape[1], arr.shape[0]),
378
+ np.ascontiguousarray(arr).tobytes(),
379
+ )
380
+ await self._send_frame(
381
+ b"\x01" + await asyncio.to_thread(_pil_to_png_bytes, img)
382
+ )
383
+ return
384
+ await self._send_frame(self._array_packet(np, arr, premultiplied))
385
+
386
+ @staticmethod
387
+ def _array_packet(np: Any, arr: Any, premultiplied: bool) -> bytes:
388
+ if not premultiplied and (arr[:, :, 3] != 255).any():
389
+ # Flutter's rgba8888 upload assumes premultiplied alpha.
390
+ out = arr.copy()
391
+ a = out[:, :, 3:4].astype(np.uint16)
392
+ out[:, :, :3] = (out[:, :, :3].astype(np.uint16) * a // 255).astype(
393
+ np.uint8
394
+ )
395
+ arr = out
396
+ arr = np.ascontiguousarray(arr)
397
+ return _encode_raw_packet(arr.shape[1], arr.shape[0], arr.tobytes())
398
+
399
+
400
+ # -- Pillow helpers (Pillow is an optional dependency; import lazily) ---------
401
+
402
+
403
+ def _premultiply_pil(image: Any) -> Any:
404
+ """
405
+ Premultiplies RGB by alpha in an RGBA Pillow image.
406
+
407
+ Flutter's rgba8888 texture upload assumes premultiplied alpha, while
408
+ Pillow produces straight alpha. All per-pixel work happens in Pillow's
409
+ C loops: an opaque image is detected with `getextrema()` and returned
410
+ as-is; otherwise each color band is multiplied by the alpha band.
411
+ """
412
+ alpha = image.getchannel("A")
413
+ if alpha.getextrema() == (255, 255):
414
+ return image
415
+ from PIL import Image as PILImage
416
+ from PIL import ImageChops
417
+
418
+ r, g, b, a = image.split()
419
+ return PILImage.merge(
420
+ "RGBA",
421
+ (
422
+ ImageChops.multiply(r, a),
423
+ ImageChops.multiply(g, a),
424
+ ImageChops.multiply(b, a),
425
+ a,
426
+ ),
427
+ )
428
+
429
+
430
+ def _pil_from_rgba(width: int, height: int, pixels: bytes) -> Any:
431
+ try:
432
+ from PIL import Image as PILImage
433
+ except ImportError:
434
+ raise RuntimeError(
435
+ "Pillow is required to premultiply raw RGBA pixels; install it "
436
+ "or pass premultiplied pixels"
437
+ ) from None
438
+ return PILImage.frombuffer("RGBA", (width, height), pixels)
439
+
440
+
441
+ def _try_pil_from_rgba(width: int, height: int, pixels: bytes) -> Optional[Any]:
442
+ try:
443
+ from PIL import Image as PILImage
444
+ except ImportError:
445
+ return None
446
+ return PILImage.frombuffer("RGBA", (width, height), pixels)
447
+
448
+
449
+ def _rgba_is_opaque(pixels: bytes) -> bool:
450
+ """
451
+ Whether every alpha byte in tightly packed RGBA8888 is 255.
452
+
453
+ The strided slice and count both run in C — no per-pixel Python loop.
454
+ """
455
+ alphas = pixels[3::4]
456
+ return alphas.count(255) == len(alphas)
457
+
458
+
459
+ def _pil_to_png_bytes(image: Any) -> bytes:
460
+ buf = io.BytesIO()
461
+ image.save(buf, format="PNG")
462
+ return buf.getvalue()
463
+
464
+
465
+ def _encode_raw_packet(width: int, height: int, rgba: bytes) -> bytes:
466
+ """
467
+ Encodes a raw full frame into a 0x04 wire packet:
468
+ `[0x04][width u32 LE][height u32 LE][premultiplied RGBA8888]`.
469
+ """
470
+ pkt = bytearray(9 + len(rgba))
471
+ pkt[0] = 0x04
472
+ pkt[1:5] = width.to_bytes(4, "little")
473
+ pkt[5:9] = height.to_bytes(4, "little")
474
+ pkt[9:] = rgba
475
+ return bytes(pkt)
@@ -63,25 +63,27 @@ class WebRenderer(Enum):
63
63
  """
64
64
  Lets the runtime choose the renderer automatically.
65
65
 
66
- This is the recommended default. In WebAssembly builds, the runtime can prefer
67
- skwasm and fall back to CanvasKit when needed. In default web builds, CanvasKit
68
- is used.
66
+ In WebAssembly builds, Chromium-based browsers prefer skwasm and other
67
+ browsers fall back to CanvasKit; in default web builds, CanvasKit is used.
69
68
  """
70
69
  CANVAS_KIT = "canvaskit"
71
70
  """
72
71
  Uses the CanvasKit renderer.
73
72
 
74
- CanvasKit is broadly compatible with modern browsers and is the renderer used by
75
- default in standard web builds.
73
+ CanvasKit is broadly compatible with modern browsers and is the default
74
+ renderer for web builds. Compared to skwasm, it is significantly faster
75
+ at exchanging byte buffers between JavaScript and Dart, which Flet apps
76
+ do on every UI update.
76
77
  """
77
78
  SKWASM = "skwasm"
78
79
  """
79
80
  Uses the skwasm renderer.
80
81
 
81
- skwasm is available for WebAssembly web builds and can provide better startup
82
- time and frame performance. Browsers and servers must meet the requirements for
83
- WebAssembly support, and multi-threaded rendering additionally requires the
84
- necessary SharedArrayBuffer security setup.
82
+ skwasm is available for WebAssembly web builds and can provide better
83
+ rendering performance, at the cost of slower byte-buffer exchange across
84
+ the JavaScript/Dart boundary. Browsers and servers must meet the
85
+ requirements for WebAssembly support, and multi-threaded rendering
86
+ additionally requires the necessary SharedArrayBuffer security setup.
85
87
  """
86
88
 
87
89
 
@@ -21,6 +21,11 @@ class Connection:
21
21
  def __init__(self):
22
22
  self.page_name: str = ""
23
23
  self.page_url: Optional[str] = None
24
+ # True when the connected client runs on the same machine (or in the
25
+ # same process), so DataChannel bytes move at local-IPC / memcpy
26
+ # speed. Widgets may use this to prefer uncompressed payloads over
27
+ # encoded ones.
28
+ self.local_data_transport: bool = False
24
29
  self.__pubsubhub = None
25
30
  self.__loop: Optional[AbstractEventLoop] = None
26
31
  self.__executor: Optional[ThreadPoolExecutor] = None
@@ -82,6 +82,8 @@ class FletDartBridgeServer(Connection):
82
82
  self.__inbound_queue: asyncio.Queue[bytes] = asyncio.Queue()
83
83
  self.__inbound_task: Optional[asyncio.Task] = None
84
84
  self.__running_tasks: set[asyncio.Task] = set()
85
+ # In-process transport — DataChannel bytes move at memcpy speed.
86
+ self.local_data_transport = True
85
87
  self.loop = loop
86
88
  self.executor = executor
87
89
  self.pubsubhub = PubSubHub(loop=loop, executor=executor)