flet 0.85.1__tar.gz → 0.85.3.dev0__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.
Files changed (316) hide show
  1. {flet-0.85.1 → flet-0.85.3.dev0}/PKG-INFO +9 -9
  2. {flet-0.85.1 → flet-0.85.3.dev0}/pyproject.toml +9 -9
  3. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/router.py +279 -76
  4. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/icon.py +1 -1
  5. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/keys.py +1 -1
  6. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/radio_group.py +2 -2
  7. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/page.py +2 -2
  8. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/types.py +1 -1
  9. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/messaging/session_store.py +3 -3
  10. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/testing/flet_test_app.py +10 -10
  11. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/object_model.py +4 -4
  12. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/version.py +1 -1
  13. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet.egg-info/PKG-INFO +9 -9
  14. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet.egg-info/SOURCES.txt +3 -0
  15. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet.egg-info/requires.txt +8 -8
  16. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_component_diff.py +8 -8
  17. flet-0.85.3.dev0/tests/test_router_modal.py +180 -0
  18. flet-0.85.3.dev0/tests/test_router_pop.py +94 -0
  19. flet-0.85.3.dev0/tests/test_router_recursive.py +251 -0
  20. {flet-0.85.1 → flet-0.85.3.dev0}/README.md +0 -0
  21. {flet-0.85.1 → flet-0.85.3.dev0}/setup.cfg +0 -0
  22. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/__init__.py +0 -0
  23. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/app.py +0 -0
  24. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/__init__.py +0 -0
  25. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/authorization.py +0 -0
  26. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/authorization_service.py +0 -0
  27. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/group.py +0 -0
  28. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/oauth_provider.py +0 -0
  29. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/oauth_token.py +0 -0
  30. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/providers/__init__.py +0 -0
  31. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/providers/auth0_oauth_provider.py +0 -0
  32. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/providers/azure_oauth_provider.py +0 -0
  33. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/providers/github_oauth_provider.py +0 -0
  34. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/providers/google_oauth_provider.py +0 -0
  35. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/auth/user.py +0 -0
  36. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/canvas/__init__.py +0 -0
  37. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/cli.py +0 -0
  38. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/__init__.py +0 -0
  39. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/component.py +0 -0
  40. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/component_decorator.py +0 -0
  41. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/component_owned.py +0 -0
  42. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/__init__.py +0 -0
  43. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/hook.py +0 -0
  44. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/use_callback.py +0 -0
  45. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/use_context.py +0 -0
  46. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/use_dialog.py +0 -0
  47. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/use_effect.py +0 -0
  48. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/use_memo.py +0 -0
  49. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/use_ref.py +0 -0
  50. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/hooks/use_state.py +0 -0
  51. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/memo.py +0 -0
  52. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/observable.py +0 -0
  53. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/public_utils.py +0 -0
  54. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/components/utils.py +0 -0
  55. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/__init__.py +0 -0
  56. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/adaptive_control.py +0 -0
  57. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/alignment.py +0 -0
  58. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/animation.py +0 -0
  59. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/base_control.py +0 -0
  60. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/base_page.py +0 -0
  61. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/blur.py +0 -0
  62. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/border.py +0 -0
  63. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/border_radius.py +0 -0
  64. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/box.py +0 -0
  65. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/buttons.py +0 -0
  66. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/colors.py +0 -0
  67. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/context.py +0 -0
  68. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/control.py +0 -0
  69. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/control_event.py +0 -0
  70. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/control_state.py +0 -0
  71. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/__init__.py +0 -0
  72. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/animated_switcher.py +0 -0
  73. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/autofill_group.py +0 -0
  74. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/__init__.py +0 -0
  75. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/arc.py +0 -0
  76. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/canvas.py +0 -0
  77. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/circle.py +0 -0
  78. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/color.py +0 -0
  79. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/fill.py +0 -0
  80. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/image.py +0 -0
  81. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/line.py +0 -0
  82. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/oval.py +0 -0
  83. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/path.py +0 -0
  84. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/points.py +0 -0
  85. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/rect.py +0 -0
  86. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/shadow.py +0 -0
  87. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/shape.py +0 -0
  88. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/canvas/text.py +0 -0
  89. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/column.py +0 -0
  90. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/dismissible.py +0 -0
  91. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/drag_target.py +0 -0
  92. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/draggable.py +0 -0
  93. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/flet_app.py +0 -0
  94. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/gesture_detector.py +0 -0
  95. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/grid_view.py +0 -0
  96. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/hero.py +0 -0
  97. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/image.py +0 -0
  98. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/interactive_viewer.py +0 -0
  99. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/keyboard_listener.py +0 -0
  100. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/list_view.py +0 -0
  101. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/markdown.py +0 -0
  102. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/merge_semantics.py +0 -0
  103. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/page_view.py +0 -0
  104. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/pagelet.py +0 -0
  105. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/placeholder.py +0 -0
  106. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/reorderable_drag_handle.py +0 -0
  107. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/responsive_row.py +0 -0
  108. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/rotated_box.py +0 -0
  109. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/row.py +0 -0
  110. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/safe_area.py +0 -0
  111. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/screenshot.py +0 -0
  112. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/semantics.py +0 -0
  113. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/shader_mask.py +0 -0
  114. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/shimmer.py +0 -0
  115. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/stack.py +0 -0
  116. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/text.py +0 -0
  117. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/text_span.py +0 -0
  118. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/transparent_pointer.py +0 -0
  119. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/view.py +0 -0
  120. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/window.py +0 -0
  121. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/core/window_drag_area.py +0 -0
  122. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/__init__.py +0 -0
  123. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_action_sheet.py +0 -0
  124. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_action_sheet_action.py +0 -0
  125. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_activity_indicator.py +0 -0
  126. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_alert_dialog.py +0 -0
  127. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_app_bar.py +0 -0
  128. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_bottom_sheet.py +0 -0
  129. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_button.py +0 -0
  130. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_checkbox.py +0 -0
  131. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_colors.py +0 -0
  132. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_context_menu.py +0 -0
  133. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_context_menu_action.py +0 -0
  134. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_date_picker.py +0 -0
  135. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_dialog_action.py +0 -0
  136. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_filled_button.py +0 -0
  137. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_icons.json +0 -0
  138. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_icons.py +0 -0
  139. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_icons.pyi +0 -0
  140. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_list_tile.py +0 -0
  141. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_navigation_bar.py +0 -0
  142. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_picker.py +0 -0
  143. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_radio.py +0 -0
  144. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_segmented_button.py +0 -0
  145. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_slider.py +0 -0
  146. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_sliding_segmented_button.py +0 -0
  147. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_switch.py +0 -0
  148. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_textfield.py +0 -0
  149. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_timer_picker.py +0 -0
  150. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/cupertino/cupertino_tinted_button.py +0 -0
  151. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/device_info.py +0 -0
  152. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/dialog_control.py +0 -0
  153. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/duration.py +0 -0
  154. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/embed_json_encoder.py +0 -0
  155. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/events.py +0 -0
  156. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/exceptions.py +0 -0
  157. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/geometry.py +0 -0
  158. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/gradients.py +0 -0
  159. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/icon_data.py +0 -0
  160. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/id_counter.py +0 -0
  161. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/layout_control.py +0 -0
  162. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/margin.py +0 -0
  163. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/__init__.py +0 -0
  164. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/alert_dialog.py +0 -0
  165. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/app_bar.py +0 -0
  166. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/auto_complete.py +0 -0
  167. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/badge.py +0 -0
  168. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/banner.py +0 -0
  169. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/bottom_app_bar.py +0 -0
  170. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/bottom_sheet.py +0 -0
  171. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/button.py +0 -0
  172. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/card.py +0 -0
  173. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/checkbox.py +0 -0
  174. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/chip.py +0 -0
  175. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/circle_avatar.py +0 -0
  176. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/container.py +0 -0
  177. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/context_menu.py +0 -0
  178. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/datatable.py +0 -0
  179. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/date_picker.py +0 -0
  180. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/date_range_picker.py +0 -0
  181. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/divider.py +0 -0
  182. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/dropdown.py +0 -0
  183. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/dropdownm2.py +0 -0
  184. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/elevated_button.py +0 -0
  185. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/expansion_panel.py +0 -0
  186. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/expansion_tile.py +0 -0
  187. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/filled_button.py +0 -0
  188. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/filled_tonal_button.py +0 -0
  189. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/floating_action_button.py +0 -0
  190. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/form_field_control.py +0 -0
  191. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/icon_button.py +0 -0
  192. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/icons.json +0 -0
  193. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/icons.py +0 -0
  194. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/icons.pyi +0 -0
  195. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/list_tile.py +0 -0
  196. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/menu_bar.py +0 -0
  197. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/menu_item_button.py +0 -0
  198. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/navigation_bar.py +0 -0
  199. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/navigation_drawer.py +0 -0
  200. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/navigation_rail.py +0 -0
  201. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/outlined_button.py +0 -0
  202. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/popup_menu_button.py +0 -0
  203. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/progress_bar.py +0 -0
  204. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/progress_ring.py +0 -0
  205. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/radio.py +0 -0
  206. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/range_slider.py +0 -0
  207. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/reorderable_list_view.py +0 -0
  208. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/search_bar.py +0 -0
  209. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/segmented_button.py +0 -0
  210. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/selection_area.py +0 -0
  211. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/slider.py +0 -0
  212. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/snack_bar.py +0 -0
  213. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/submenu_button.py +0 -0
  214. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/switch.py +0 -0
  215. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/tabs.py +0 -0
  216. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/text_button.py +0 -0
  217. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/textfield.py +0 -0
  218. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/time_picker.py +0 -0
  219. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/tooltip.py +0 -0
  220. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/material/vertical_divider.py +0 -0
  221. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/multi_view.py +0 -0
  222. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/object_patch.py +0 -0
  223. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/padding.py +0 -0
  224. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/painting.py +0 -0
  225. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/query_string.py +0 -0
  226. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/ref.py +0 -0
  227. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/scrollable_control.py +0 -0
  228. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/__init__.py +0 -0
  229. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/accelerometer.py +0 -0
  230. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/barometer.py +0 -0
  231. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/battery.py +0 -0
  232. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/browser_context_menu.py +0 -0
  233. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/clipboard.py +0 -0
  234. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/connectivity.py +0 -0
  235. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/file_picker.py +0 -0
  236. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/gyroscope.py +0 -0
  237. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/haptic_feedback.py +0 -0
  238. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/magnetometer.py +0 -0
  239. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/screen_brightness.py +0 -0
  240. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/semantics_service.py +0 -0
  241. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/sensor_error_event.py +0 -0
  242. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/service.py +0 -0
  243. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/shake_detector.py +0 -0
  244. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/share.py +0 -0
  245. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/shared_preferences.py +0 -0
  246. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/storage_paths.py +0 -0
  247. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/url_launcher.py +0 -0
  248. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/user_accelerometer.py +0 -0
  249. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/services/wakelock.py +0 -0
  250. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/template_route.py +0 -0
  251. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/text_style.py +0 -0
  252. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/theme.py +0 -0
  253. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/transform.py +0 -0
  254. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/controls/value_types.py +0 -0
  255. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/fastapi/__init__.py +0 -0
  256. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/messaging/connection.py +0 -0
  257. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/messaging/flet_socket_server.py +0 -0
  258. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/messaging/protocol.py +0 -0
  259. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/messaging/pyodide_connection.py +0 -0
  260. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/messaging/session.py +0 -0
  261. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/pubsub/__init__.py +0 -0
  262. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/pubsub/pubsub_client.py +0 -0
  263. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/pubsub/pubsub_hub.py +0 -0
  264. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/py.typed +0 -0
  265. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/security/__init__.py +0 -0
  266. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/testing/__init__.py +0 -0
  267. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/testing/finder.py +0 -0
  268. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/testing/tester.py +0 -0
  269. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/__init__.py +0 -0
  270. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/browser.py +0 -0
  271. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/classproperty.py +0 -0
  272. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/deprecated.py +0 -0
  273. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/deprecated_enum.py +0 -0
  274. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/files.py +0 -0
  275. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/from_dict.py +0 -0
  276. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/griffe_deprecations.py +0 -0
  277. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/hashing.py +0 -0
  278. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/json_utils.py +0 -0
  279. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/linux_deps.py +0 -0
  280. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/locks.py +0 -0
  281. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/network.py +0 -0
  282. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/once.py +0 -0
  283. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/pip.py +0 -0
  284. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/platform_utils.py +0 -0
  285. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/slugify.py +0 -0
  286. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/strings.py +0 -0
  287. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/typing_utils.py +0 -0
  288. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/validation.py +0 -0
  289. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet/utils/vector.py +0 -0
  290. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet.egg-info/dependency_links.txt +0 -0
  291. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet.egg-info/entry_points.txt +0 -0
  292. {flet-0.85.1 → flet-0.85.3.dev0}/src/flet.egg-info/top_level.txt +0 -0
  293. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_auth_lazy_imports.py +0 -0
  294. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_base_control.py +0 -0
  295. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_colors.py +0 -0
  296. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_component_effects.py +0 -0
  297. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_component_renderer.py +0 -0
  298. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_dataclasses.py +0 -0
  299. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_datetime.py +0 -0
  300. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_deprecated.py +0 -0
  301. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_events.py +0 -0
  302. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_from_dict.py +0 -0
  303. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_griffe_deprecations.py +0 -0
  304. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_icons.py +0 -0
  305. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_object_diff_frozen.py +0 -0
  306. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_object_diff_in_place.py +0 -0
  307. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_object_diff_memory_churn.py +0 -0
  308. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_object_diff_performance.py +0 -0
  309. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_observable.py +0 -0
  310. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_patch_dataclass.py +0 -0
  311. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_session_disconnect_buffering.py +0 -0
  312. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_skip_double_update.py +0 -0
  313. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_use_dialog.py +0 -0
  314. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_validation.py +0 -0
  315. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_validation_benchmark.py +0 -0
  316. {flet-0.85.1 → flet-0.85.3.dev0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet
3
- Version: 0.85.1
3
+ Version: 0.85.3.dev0
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.85.1; extra == "cli"
13
- Requires-Dist: flet-web==0.85.1; extra == "web"
12
+ Requires-Dist: flet-cli==0.85.3.dev0; extra == "cli"
13
+ Requires-Dist: flet-web==0.85.3.dev0; 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.85.1; extra == "all"
21
- Requires-Dist: flet-web==0.85.1; extra == "all"
22
- Requires-Dist: flet-desktop==0.85.1; extra == "all"
20
+ Requires-Dist: flet-cli==0.85.3.dev0; extra == "all"
21
+ Requires-Dist: flet-web==0.85.3.dev0; extra == "all"
22
+ Requires-Dist: flet-desktop==0.85.3.dev0; extra == "all"
23
23
  Provides-Extra: cli
24
- Requires-Dist: flet-cli==0.85.1; extra == "cli"
24
+ Requires-Dist: flet-cli==0.85.3.dev0; extra == "cli"
25
25
  Provides-Extra: desktop
26
- Requires-Dist: flet-desktop==0.85.1; extra == "desktop"
26
+ Requires-Dist: flet-desktop==0.85.3.dev0; extra == "desktop"
27
27
  Provides-Extra: web
28
- Requires-Dist: flet-web==0.85.1; extra == "web"
28
+ Requires-Dist: flet-web==0.85.3.dev0; extra == "web"
29
29
 
30
30
  <p align="center">
31
31
  <a href="https://flet.dev"><img src="https://raw.githubusercontent.com/flet-dev/flet/refs/heads/main/media/logo/flet-logo.svg" height="150" alt="Flet logo"></a>
@@ -1,14 +1,14 @@
1
1
  [project]
2
2
  name = "flet"
3
- version = "0.85.1"
3
+ version = "0.85.3.dev0"
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.85.1; extra == 'cli'",
11
- "flet-web==0.85.1; extra == 'web'",
10
+ "flet-cli==0.85.3.dev0; extra == 'cli'",
11
+ "flet-web==0.85.3.dev0; 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.85.1",
27
- "flet-web==0.85.1",
28
- "flet-desktop==0.85.1",
26
+ "flet-cli==0.85.3.dev0",
27
+ "flet-web==0.85.3.dev0",
28
+ "flet-desktop==0.85.3.dev0",
29
29
  ]
30
- cli = ["flet-cli==0.85.1"]
30
+ cli = ["flet-cli==0.85.3.dev0"]
31
31
  desktop = [
32
- "flet-desktop==0.85.1",
32
+ "flet-desktop==0.85.3.dev0",
33
33
  ]
34
- web = ["flet-web==0.85.1"]
34
+ web = ["flet-web==0.85.3.dev0"]
35
35
 
36
36
  [project.scripts]
37
37
  flet = "flet.cli:main"
@@ -47,30 +47,55 @@ class Route:
47
47
  """
48
48
  Defines a single route in the route tree.
49
49
 
50
- Routes can be nested via ``children`` to create layout hierarchies.
51
- A route with ``component`` and ``children`` acts as a layout route — its
50
+ Routes can be nested via `children` to create layout hierarchies.
51
+ A route with `component` and `children` acts as a layout route — its
52
52
  component should call :func:`~flet.use_route_outlet` to render the
53
53
  matched child.
54
54
 
55
55
  Args:
56
- path: Relative path segment. Supports dynamic segments (``:name``),
57
- optional segments (``:name?``), splats (``:name*``), and custom
58
- regex constraints (``:name(\\\\d+)``). ``None`` for pathless
56
+ path: Relative path segment. Supports dynamic segments (`:name`),
57
+ optional segments (`:name?`), splats (`:name*`), and custom
58
+ regex constraints (`:name(\\\\d+)`). `None` for pathless
59
59
  layout routes.
60
- index: When ``True``, this route matches when the parent path
60
+ index: When `True`, this route matches when the parent path
61
61
  matches exactly (no further segments). Index routes must not
62
- have ``path`` or ``children``.
63
- component: A ``@component`` function to render when this route
62
+ have `path` or `children`.
63
+ component: A `@component` function to render when this route
64
64
  matches.
65
65
  children: Nested child routes.
66
66
  loader: Optional data loader function. Called with the matched
67
67
  params dict when the route matches. Result is available via
68
68
  :func:`~flet.use_route_loader_data`.
69
- outlet: When ``True`` and ``manage_views=True``, this route acts
69
+ outlet: When `True` and `manage_views=True`, this route acts
70
70
  as a layout that wraps its matched child via
71
71
  :func:`~flet.use_route_outlet` within a single
72
72
  :class:`~flet.View`, instead of each child becoming a separate
73
73
  :class:`~flet.View`.
74
+ modal: When `True` and `manage_views=True`, the route's View
75
+ is rendered as a modal overlay on top of the existing stack
76
+ instead of replacing it. The route's component should set
77
+ `fullscreen_dialog=True` on its returned :class:`~flet.View`
78
+ so Flutter renders the slide-up presentation and a close (X)
79
+ icon. Placement controls the base stack:
80
+
81
+ * Declared at the **top level**: a *global* modal. The base
82
+ stack is rebuilt from the last non-modal location the
83
+ Router saw (defaults to `"/"` on first render).
84
+ * Declared as a **child** of a non-modal route: a *local*
85
+ modal. The base stack is the chain above the modal in the
86
+ route tree, so deep-link works from the URL alone.
87
+
88
+ On pop, the Router navigates to the resolved URL of the
89
+ non-modal parent — never to `views[-2].route` — so a
90
+ modal close is always a real navigation back to the
91
+ base location.
92
+ recursive: When `True`, the route can match itself as its own
93
+ descendant — one matched `_RouteMatch` per consumed segment.
94
+ Useful for tree-shaped URLs with unbounded depth
95
+ (e.g. `/folder/a/b/c`) where each segment should become its
96
+ own stack entry. Non-recursive children are tried before
97
+ self-recursion at every depth so a more specific sibling
98
+ (e.g. `example/:gp*`) wins over the recursive `:slug`.
74
99
  """
75
100
 
76
101
  path: str | None = None
@@ -79,6 +104,8 @@ class Route:
79
104
  children: list[Route] | None = field(default=None)
80
105
  loader: Callable[..., Any] | None = None
81
106
  outlet: bool = False
107
+ modal: bool = False
108
+ recursive: bool = False
82
109
 
83
110
 
84
111
  @dataclass
@@ -87,9 +114,9 @@ class LocationInfo:
87
114
  Describes the current location parsed from the page route.
88
115
 
89
116
  Args:
90
- pathname: URL path portion (e.g. ``/products/42``).
91
- search: Query string portion without leading ``?``.
92
- hash: Fragment portion without leading ``#``.
117
+ pathname: URL path portion (e.g. `/products/42`).
118
+ search: Query string portion without leading `?`.
119
+ hash: Fragment portion without leading `#`.
93
120
  """
94
121
 
95
122
  pathname: str
@@ -142,7 +169,7 @@ def _match_routes(
142
169
  Match pathname against route tree.
143
170
 
144
171
  Returns a chain of matched routes from outermost to innermost,
145
- or ``None`` if no route matches.
172
+ or `None` if no route matches.
146
173
  """
147
174
  for route in routes:
148
175
  result = _try_match(route, pathname, parent_path)
@@ -192,6 +219,43 @@ def _try_match(
192
219
  if not full_path:
193
220
  full_path = "/"
194
221
 
222
+ if route.recursive:
223
+ # Recursive routes consume one matched segment per recursion and
224
+ # try non-recursive children before self-recursing — so a more
225
+ # specific sibling (e.g. `example/:gp*`) wins over the
226
+ # recursive `:slug` at every depth without duplicate
227
+ # declarations.
228
+ prefix_pattern = repath.pattern(full_path, end=False)
229
+ prefix_m = re.match(prefix_pattern, pathname)
230
+ if not prefix_m:
231
+ return None
232
+ consumed = prefix_m.group(0)
233
+ head = _RouteMatch(
234
+ route=route,
235
+ params=prefix_m.groupdict(),
236
+ full_path=full_path,
237
+ resolved_path=consumed,
238
+ )
239
+ # Fully consumed → this is the leaf. Accept either an exact
240
+ # match or a trailing "/" remainder (so /folder/a and
241
+ # /folder/a/ both terminate cleanly).
242
+ remainder = pathname[len(consumed) :]
243
+ if not remainder or remainder == "/":
244
+ return [head]
245
+ # 1) Try non-recursive children first (more specific match).
246
+ for child in route.children or []:
247
+ if child is route:
248
+ continue
249
+ child_result = _try_match(child, pathname, consumed)
250
+ if child_result is not None:
251
+ return [head] + child_result
252
+ # 2) Fall back to self-recursion with the consumed prefix as
253
+ # the parent path.
254
+ recurse = _try_match(route, pathname, consumed)
255
+ if recurse is None:
256
+ return None
257
+ return [head] + recurse
258
+
195
259
  if route.children:
196
260
  # Parent route — try prefix match to extract params, then match children
197
261
  parent_params: dict[str, str] = {}
@@ -298,7 +362,7 @@ def use_route_location() -> str:
298
362
  Returns an empty string if called outside a Router tree.
299
363
 
300
364
  Returns:
301
- The current URL pathname (e.g. ``"/products/42"``).
365
+ The current URL pathname (e.g. `"/products/42"`).
302
366
  """
303
367
  loc = use_context(_location_context)
304
368
  if not _is_inside_router(loc):
@@ -311,17 +375,17 @@ def use_view_path() -> str:
311
375
  Returns the resolved URL for the current view level.
312
376
 
313
377
  This differs from :func:`use_route_location` which returns the full
314
- current URL. ``use_view_path`` returns the URL up to the current
315
- view level (the leaf route of the view in ``manage_views=True`` mode).
378
+ current URL. `use_view_path` returns the URL up to the current
379
+ view level (the leaf route of the view in `manage_views=True` mode).
316
380
 
317
- Useful as the ``route`` value for :class:`~flet.View` to produce a
381
+ Useful as the `route` value for :class:`~flet.View` to produce a
318
382
  unique Navigator key per view in the stack.
319
383
 
320
384
  Must be called inside a component rendered by a :class:`~flet.Router`.
321
385
  Returns an empty string if called outside a Router tree.
322
386
 
323
387
  Returns:
324
- The resolved URL for this view level (e.g. ``"/products/42"``).
388
+ The resolved URL for this view level (e.g. `"/products/42"`).
325
389
  """
326
390
  value = use_context(_view_path_context)
327
391
  if not _is_inside_router(value):
@@ -335,10 +399,10 @@ def use_route_outlet() -> Control:
335
399
 
336
400
  Used inside layout route components to render the active child route.
337
401
  Must be called inside a component rendered by a :class:`~flet.Router`.
338
- Returns ``None`` if called outside a Router tree.
402
+ Returns `None` if called outside a Router tree.
339
403
 
340
404
  Returns:
341
- The rendered child component, or ``None`` if no child matches.
405
+ The rendered child component, or `None` if no child matches.
342
406
  """
343
407
  value = use_context(_outlet_context)
344
408
  if not _is_inside_router(value):
@@ -350,10 +414,10 @@ def use_route_loader_data():
350
414
  """
351
415
  Returns the data from the current route's loader function.
352
416
 
353
- Must be called inside a component whose route has a ``loader`` defined.
417
+ Must be called inside a component whose route has a `loader` defined.
354
418
 
355
419
  Returns:
356
- The return value of the route's loader function, or ``None``.
420
+ The return value of the route's loader function, or `None`.
357
421
  """
358
422
  value = use_context(_loader_data_context)
359
423
  if not _is_inside_router(value):
@@ -369,12 +433,12 @@ def is_route_active(path: str, exact: bool = False) -> bool:
369
433
 
370
434
  Args:
371
435
  path: Path to check against the current location.
372
- exact: If ``True``, requires an exact match. If ``False`` (default),
373
- a prefix match is used (e.g. ``"/products"`` matches
374
- ``"/products/42"``).
436
+ exact: If `True`, requires an exact match. If `False` (default),
437
+ a prefix match is used (e.g. `"/products"` matches
438
+ `"/products/42"`).
375
439
 
376
440
  Returns:
377
- ``True`` if the path matches the current location.
441
+ `True` if the path matches the current location.
378
442
  """
379
443
  loc = use_context(_location_context)
380
444
  if not _is_inside_router(loc):
@@ -443,7 +507,7 @@ def _split_chain_into_view_levels(
443
507
  """
444
508
  Split matched chain into layout wrappers and view entries.
445
509
 
446
- Only **pathless** routes (no ``path``, not ``index``) with a component
510
+ Only **pathless** routes (no `path`, not `index`) with a component
447
511
  are treated as layouts that wrap every View. All other routes with
448
512
  a component become their own View in the stack.
449
513
 
@@ -451,7 +515,7 @@ def _split_chain_into_view_levels(
451
515
  A tuple of (layouts, view_entries) where:
452
516
  - layouts: pathless routes with components that wrap every View
453
517
  - view_entries: path-bearing routes, each becoming its own View.
454
- Each entry is ``(match, accumulated_path)``.
518
+ Each entry is `(match, accumulated_path)`.
455
519
  """
456
520
  layouts: list[_RouteMatch] = []
457
521
  view_entries: list[tuple[_RouteMatch, str]] = []
@@ -543,7 +607,7 @@ def Router(
543
607
  Navigation is done via :meth:`~flet.Page.push_route` or
544
608
  :meth:`~flet.Page.navigate`.
545
609
 
546
- When ``manage_views`` is ``True``, the Router returns a list of
610
+ When `manage_views` is `True`, the Router returns a list of
547
611
  :class:`~flet.View` objects (one per path level) instead of a single
548
612
  component tree. This enables swipe-back gestures, system back button,
549
613
  and :class:`~flet.AppBar` implicit back button on mobile.
@@ -552,10 +616,10 @@ def Router(
552
616
  Args:
553
617
  routes: List of top-level :class:`~flet.Route` definitions.
554
618
  not_found: Optional component to render when no route matches (404).
555
- manage_views: When ``True``, produce a list of
619
+ manage_views: When `True`, produce a list of
556
620
  :class:`~flet.View` objects (one per path level) instead of a
557
621
  single component tree. Route components should return
558
- :class:`~flet.View` instances with ``route`` and ``appbar``
622
+ :class:`~flet.View` instances with `route` and `appbar`
559
623
  set. Use with :meth:`~flet.Page.render_views`.
560
624
 
561
625
  Example:
@@ -576,6 +640,22 @@ def Router(
576
640
  location, set_location = use_state(page.route or "/")
577
641
  prev_route_handler_ref = use_ref(None)
578
642
  prev_pop_handler_ref = use_ref(None)
643
+ # Last non-modal location the Router saw. Used as the base for
644
+ # global modal routes (declared at the top level) — when the user
645
+ # navigates from /apps/X to /settings (modal=True), the modal is
646
+ # rendered ON TOP OF the chain matched against /apps/X, so closing
647
+ # it reveals the original stack instead of rebuilding it.
648
+ prev_non_modal_location_ref = use_ref(None)
649
+ # Snapshot of the chain emitted for the current location. Used by
650
+ # the default pop handler to compute the parent URL structurally
651
+ # (`chain[-2].resolved_path`), which survives shared `view.route`
652
+ # keys (e.g. an app that uses the same route key for two sibling
653
+ # tab roots to suppress switch animations).
654
+ current_chain_ref = use_ref(None)
655
+ # URL to navigate to when popping the topmost view, IF the current
656
+ # chain ends in a modal route. `None` otherwise. Set whenever a
657
+ # modal route matches.
658
+ current_modal_pop_to_ref = use_ref(None)
579
659
 
580
660
  # Subscribe to route changes on mount
581
661
  def setup_listeners():
@@ -590,13 +670,45 @@ def Router(
590
670
  prev_pop_handler_ref.current = page.on_view_pop
591
671
 
592
672
  def on_view_pop(e):
593
- from flet.components.public_utils import unwrap_component
594
-
595
- views_list = unwrap_component(page.views)
596
- if isinstance(views_list, list) and len(views_list) > 1:
597
- prev_view = unwrap_component(views_list[-2])
598
- if prev_view is not None:
599
- page.navigate(prev_view.route)
673
+ # If the app installed its own `page.on_view_pop` before
674
+ # the Router mounted, delegate to it. Apps can still
675
+ # override entirely (e.g. for a stack visualisation or
676
+ # confirmation flow).
677
+ prev = prev_pop_handler_ref.current
678
+ if prev is not None:
679
+ prev(e)
680
+ return
681
+
682
+ # Modal pop: navigate to the base URL stamped at emit
683
+ # time. Same logic for global and local modals — the
684
+ # base URL was resolved when the modal was matched.
685
+ if current_modal_pop_to_ref.current is not None:
686
+ page.navigate(current_modal_pop_to_ref.current)
687
+ return
688
+
689
+ # Non-modal pop: navigate to the previous *view entry*'s
690
+ # resolved URL rather than `chain[-2]` or
691
+ # `views[-2].route`. Classifying into view entries skips
692
+ # `outlet=True` layouts and componentless grouping routes
693
+ # — otherwise `chain[-2]` can point at a layout whose URL
694
+ # equals the current view's URL, making the pop navigate
695
+ # to where we already are (a no-op that strands the URL).
696
+ # Staying route-tree-structural also survives shared view
697
+ # keys like a tab-root layout emitting `route="/"` for
698
+ # multiple sibling sections.
699
+ chain_now = current_chain_ref.current
700
+ if chain_now:
701
+ _, view_entries = _split_chain_into_view_levels(chain_now)
702
+ if len(view_entries) > 1:
703
+ parent_match = view_entries[-2][0]
704
+ target = (
705
+ parent_match.resolved_path or parent_match.full_path or "/"
706
+ )
707
+ page.navigate(target)
708
+ return
709
+
710
+ # Stack of length 1 — nothing to pop to. (Flutter's
711
+ # `Navigator.canPop` is False here anyway.)
600
712
 
601
713
  page.on_view_pop = on_view_pop
602
714
 
@@ -621,10 +733,66 @@ def Router(
621
733
  if manage_views:
622
734
  from flet.controls.core.view import View
623
735
 
736
+ # Reset state so a stale modal pop_to doesn't leak into
737
+ # a 404'd location.
738
+ current_chain_ref.current = None
739
+ current_modal_pop_to_ref.current = None
624
740
  return [View(route=pathname, controls=[not_found()])]
625
741
  return not_found()
626
742
  return None
627
743
 
744
+ # Detect modal routes in the chain. `modal_idx` is the index of
745
+ # the first route in the chain with `modal=True`; `-1` means
746
+ # the chain is fully non-modal.
747
+ modal_idx = -1
748
+ for i, m in enumerate(chain):
749
+ if m.route.modal:
750
+ modal_idx = i
751
+ break
752
+
753
+ # For a *global* modal (`modal_idx == 0` — the chain starts at a
754
+ # modal with no non-modal parents) we want the visible stack to be
755
+ # the chain of the previously visited non-modal location +
756
+ # the modal's own chain. This way the underlying stack stays mounted
757
+ # underneath the modal — closing the modal reveals it without a
758
+ # rebuild.
759
+ if manage_views and modal_idx == 0:
760
+ base_location = prev_non_modal_location_ref.current or "/"
761
+ base_pathname = _normalize_path(urlparse(base_location).path or "/")
762
+ base_chain = _match_routes(routes, base_pathname) or []
763
+ # If the base path also resolves to a modal (shouldn't happen
764
+ # in practice — non-modal navigations are the only ones that
765
+ # update prev_non_modal_location_ref), fall back to no base.
766
+ if any(b.route.modal for b in base_chain):
767
+ base_chain = []
768
+ # Combine: base lives below, modal sits on top.
769
+ chain = base_chain + chain
770
+ # Re-locate the modal index in the combined chain.
771
+ modal_idx = len(base_chain)
772
+
773
+ # Track refs that downstream consumers (default pop handler, etc.)
774
+ # read. `current_chain_ref` always reflects the chain we're
775
+ # actually emitting. `current_modal_pop_to_ref` is set only when
776
+ # the chain ends in a modal — non-modal navigations clear it.
777
+ current_chain_ref.current = chain
778
+ if modal_idx == -1:
779
+ # Non-modal navigation — remember it as the next base.
780
+ prev_non_modal_location_ref.current = location
781
+ current_modal_pop_to_ref.current = None
782
+ else:
783
+ # Modal route — the URL to pop to is the resolved URL of the
784
+ # last non-modal entry in the chain.
785
+ if modal_idx > 0:
786
+ parent = chain[modal_idx - 1]
787
+ current_modal_pop_to_ref.current = (
788
+ parent.resolved_path or parent.full_path or "/"
789
+ )
790
+ else:
791
+ # No non-modal parent in the chain (rare — would require a
792
+ # deep-link to a top-level modal with no remembered base
793
+ # AND an empty default match). Fall back to "/".
794
+ current_modal_pop_to_ref.current = "/"
795
+
628
796
  # Merge all params from the chain
629
797
  all_params: dict[str, str] = {}
630
798
  for m in chain:
@@ -640,7 +808,8 @@ def Router(
640
808
  loc = LocationInfo(pathname=pathname, search=search, hash=hash_val)
641
809
 
642
810
  if not manage_views:
643
- # Single-view mode (existing behavior)
811
+ # Single-view mode (existing behavior). `modal` is ignored
812
+ # here — it only affects stack composition in multi-view mode.
644
813
  return _location_context(
645
814
  loc,
646
815
  lambda: _params_context(
@@ -653,47 +822,81 @@ def Router(
653
822
  # route, etc.). The Router sets route and can_pop on each View
654
823
  # after the component body executes (via the patch walk).
655
824
  # Used with page.render_views(App).
656
- layouts, view_entries = _split_chain_into_view_levels(chain)
825
+ #
826
+ # If the chain contains a modal route, split into two independent
827
+ # sub-chains at the modal boundary. Each sub-chain is classified
828
+ # into layouts vs. view entries SEPARATELY so that an outlet route
829
+ # that would be a leaf view in its own sub-chain doesn't get
830
+ # re-classified as a layout just because there's more chain after
831
+ # the boundary. `modal_idx` already points to the modal route in
832
+ # the (possibly combined) chain.
833
+ # `modal_idx <= 0` covers both the no-modal case (`-1`) and a
834
+ # degenerate modal chain with no base (`0`) — neither is split.
835
+ sub_chains = [chain] if modal_idx <= 0 else [chain[:modal_idx], chain[modal_idx:]]
836
+
837
+ # Each sub-chain's views see their own URL via the location
838
+ # context. The base sub-chain (when there's a modal split) sees the
839
+ # remembered non-modal location it was matched against — so
840
+ # `is_route_active("/gallery")` inside a base view stays True
841
+ # while a global `/settings` modal is open over Gallery.
842
+ base_sub_loc = LocationInfo(pathname=pathname, search=search, hash=hash_val)
843
+ if modal_idx > 0:
844
+ base_loc_str = prev_non_modal_location_ref.current or "/"
845
+ base_parsed = urlparse(base_loc_str)
846
+ base_sub_loc = LocationInfo(
847
+ pathname=_normalize_path(base_parsed.path or "/"),
848
+ search=base_parsed.query or "",
849
+ hash=base_parsed.fragment or "",
850
+ )
657
851
 
658
852
  results = []
659
- for match, _ in view_entries:
660
- # Params accumulated up to this view level
661
- level_params: dict[str, str] = {}
662
- for m in chain:
663
- level_params.update(m.params)
664
- if m is match:
665
- break
666
-
667
- # LocationInfo.pathname is the actual URL (not the route template),
668
- # so is_route_active() and use_route_location() work consistently.
669
- level_loc = LocationInfo(pathname=pathname, search=search, hash=hash_val)
670
- # Per-view resolved URL — unique per view level for Navigator keying.
671
- level_view_path = match.resolved_path or match.full_path or "/"
672
- _match = match
673
- # Only apply layouts that appear before this view entry in the chain
674
- match_idx = chain.index(match)
675
- _layouts = [lm for lm in layouts if chain.index(lm) < match_idx]
676
-
677
- def build_view_content(
678
- _match=_match,
679
- _layouts=_layouts,
680
- _level_params=level_params,
681
- _level_loc=level_loc,
682
- _level_view_path=level_view_path,
683
- ):
684
- return _view_path_context(
685
- _level_view_path,
686
- lambda: _location_context(
687
- _level_loc,
688
- lambda: _params_context(
689
- _level_params,
690
- lambda: _build_view_level(
691
- _layouts, _match, loader_results, chain
853
+ for sub_chain_idx, sub_chain in enumerate(sub_chains):
854
+ layouts, view_entries = _split_chain_into_view_levels(sub_chain)
855
+ # The base sub-chain (everything before the modal) is rendered
856
+ # "as if" the user is at the previous non-modal URL; the modal
857
+ # sub-chain is rendered at the modal URL.
858
+ sub_loc = base_sub_loc if (modal_idx > 0 and sub_chain_idx == 0) else loc
859
+ for match, _ in view_entries:
860
+ # Params accumulated up to this view level — within the
861
+ # combined chain so a local modal view still sees its
862
+ # parents' captures.
863
+ level_params: dict[str, str] = {}
864
+ for m in chain:
865
+ level_params.update(m.params)
866
+ if m is match:
867
+ break
868
+
869
+ # Per-view resolved URL unique per view level for
870
+ # Navigator keying.
871
+ level_view_path = match.resolved_path or match.full_path or "/"
872
+ _match = match
873
+ # Only apply layouts that appear before this view entry IN
874
+ # THE SAME SUB-CHAIN. A modal view never inherits the
875
+ # base's layouts (and vice versa).
876
+ match_idx = sub_chain.index(match)
877
+ _layouts = [lm for lm in layouts if sub_chain.index(lm) < match_idx]
878
+
879
+ def build_view_content(
880
+ _match=_match,
881
+ _layouts=_layouts,
882
+ _level_params=level_params,
883
+ _level_loc=sub_loc,
884
+ _level_view_path=level_view_path,
885
+ _sub_chain=sub_chain,
886
+ ):
887
+ return _view_path_context(
888
+ _level_view_path,
889
+ lambda: _location_context(
890
+ _level_loc,
891
+ lambda: _params_context(
892
+ _level_params,
893
+ lambda: _build_view_level(
894
+ _layouts, _match, loader_results, _sub_chain
895
+ ),
692
896
  ),
693
897
  ),
694
- ),
695
- )
898
+ )
696
899
 
697
- results.append(build_view_content())
900
+ results.append(build_view_content())
698
901
 
699
902
  return results
@@ -28,7 +28,7 @@ class Icon(LayoutControl):
28
28
  The icon to display, selected from a predefined icon set.
29
29
 
30
30
  You can explore available icons using the
31
- [Flet Icons Browser](https://examples.flet.dev/icons_browser/).
31
+ [Flet Icons Browser](https://flet.app/gallery/run/apps/icons_browser/).
32
32
  """
33
33
 
34
34
  color: Optional[ColorValue] = None
@@ -10,7 +10,7 @@ __all__ = [
10
10
 
11
11
 
12
12
  def _lazy_value(cls=None, **kwargs):
13
- """Deferred proxy for ``value`` to avoid circular import with base_control."""
13
+ """Deferred proxy for `value` to avoid circular import with base_control."""
14
14
  from flet.controls.base_control import value as _v
15
15
 
16
16
  if cls is not None:
@@ -21,8 +21,8 @@ class RadioGroup(Control):
21
21
  """
22
22
  The content of the RadioGroup.
23
23
 
24
- Typically a list of `Radio` controls nested in a container control, e.g. `Column`,
25
- `Row`.
24
+ Typically a list of :class:`~flet.Radio` controls nested in a container control,
25
+ e.g. `Column`, `Row`.
26
26
 
27
27
  Raises:
28
28
  ValueError: If :attr:`content` is not visible.
@@ -947,8 +947,8 @@ class Page(BasePage):
947
947
  """
948
948
  Navigate to a new route (sync convenience wrapper).
949
949
 
950
- Equivalent to ``asyncio.create_task(page.push_route(route, **kwargs))``.
951
- Use this in synchronous callbacks (e.g. ``on_click``) where awaiting
950
+ Equivalent to `asyncio.create_task(page.push_route(route, **kwargs))`.
951
+ Use this in synchronous callbacks (e.g. `on_click`) where awaiting
952
952
  is not possible.
953
953
 
954
954
  Args:
@@ -1307,7 +1307,7 @@ Represents a color and can be:
1307
1307
  - a material color from the :class:`~flet.Colors` enum,
1308
1308
  - or a Cupertino color from the :class:`~flet.CupertinoColors` enum.
1309
1309
 
1310
- More information [here](https://flet.dev/docs/cookbook/cookbook/cookbook/colors).
1310
+ More information [here](https://flet.dev/docs/cookbook/colors).
1311
1311
  """
1312
1312
 
1313
1313
  # Icons