diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c17564d9..4092fe13e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Flet changelog +## 0.28.3 + +* New: Multiple subscribers can subscribe to a published topic by `send_all_on_topic` ([#5303](https://github.com/flet-dev/flet/issues/5303)) +* Fixed: Local Images Not Rendering in Android App using Flet 0.27.6 ([#5198](https://github.com/flet-dev/flet/issues/5198)) +* Fixed: FilePicker.save_file() opens blank gray screen in APK build (works fine in VS) ([#5301](https://github.com/flet-dev/flet/issues/5301)) +* Fixed: Routing / Navigation broken since flet 0.28.2 ([#5302](https://github.com/flet-dev/flet/issues/5302)) + ## 0.28.2 * Fixed missing imports in `__init__.py` ([#5292](https://github.com/flet-dev/flet/pull/5292)). diff --git a/ci/whats-new.ps1 b/ci/whats-new.ps1 index 5c77e5cf4..43b667e55 100644 --- a/ci/whats-new.ps1 +++ b/ci/whats-new.ps1 @@ -1,4 +1,4 @@ -$milestone = 15 +$milestone = 17 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 diff --git a/packages/flet/CHANGELOG.md b/packages/flet/CHANGELOG.md index 8bc2394ea..f32cda3b8 100644 --- a/packages/flet/CHANGELOG.md +++ b/packages/flet/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.28.3 + +* Fixed: Local Images Not Rendering in Android App using Flet 0.27.6 ([#5198](https://github.com/flet-dev/flet/issues/5198)) +* Fixed: FilePicker.save_file() opens blank gray screen in APK build (works fine in VS) ([#5301](https://github.com/flet-dev/flet/issues/5301)) + # 0.28.2 * Fixed missing imports in `__init__.py` ([#5292](https://github.com/flet-dev/flet/pull/5292)). diff --git a/packages/flet/lib/src/controls/file_picker.dart b/packages/flet/lib/src/controls/file_picker.dart index be474b7b1..cdcf50a54 100644 --- a/packages/flet/lib/src/controls/file_picker.dart +++ b/packages/flet/lib/src/controls/file_picker.dart @@ -180,13 +180,17 @@ class _FilePickerControlState extends State else if (state?.toLowerCase() == "savefile" && !kIsWeb) { FilePicker.platform .saveFile( - dialogTitle: dialogTitle, - fileName: fileName, - initialDirectory: initialDirectory, - lockParentWindow: true, - type: fileType, - allowedExtensions: allowedExtensions, - ) + dialogTitle: dialogTitle, + fileName: fileName != null || !isiOSPlatform() + ? fileName + : "new-file", + initialDirectory: initialDirectory, + lockParentWindow: true, + type: fileType, + allowedExtensions: allowedExtensions, + bytes: isAndroidPlatform() || isiOSPlatform() + ? Uint8List(0) + : null) .then((result) { debugPrint("saveFile() completed"); _path = result; diff --git a/packages/flet/lib/src/utils/uri.dart b/packages/flet/lib/src/utils/uri.dart index 91b0cb8b5..f0724a0e5 100644 --- a/packages/flet/lib/src/utils/uri.dart +++ b/packages/flet/lib/src/utils/uri.dart @@ -16,8 +16,7 @@ Uri getAssetUri(Uri pageUri, String assetPath) { scheme: pageUri.scheme, host: pageUri.host, port: pageUri.port, - path: pageUri.path + - (assetPath.startsWith("/") ? assetPath.substring(1) : assetPath)); + pathSegments: [...pageUri.pathSegments, ...assetPath.split("/")]); } Uri getBaseUri(Uri pageUri) { diff --git a/packages/flet/pubspec.yaml b/packages/flet/pubspec.yaml index f10430a31..c94138633 100644 --- a/packages/flet/pubspec.yaml +++ b/packages/flet/pubspec.yaml @@ -2,7 +2,7 @@ name: flet description: Write entire Flutter app in Python or add server-driven UI experience into existing Flutter app. homepage: https://flet.dev repository: https://github.com/flet-dev/flet/packages/flet -version: 0.28.2 +version: 0.28.3 # This package supports all platforms listed below. platforms: @@ -34,7 +34,7 @@ dependencies: flutter_highlight: ^0.7.0 highlight: ^0.7.0 markdown: ^7.2.2 - file_picker: ^8.1.3 + file_picker: ^10.1.9 shared_preferences: ^2.3.2 flutter_svg: ^2.0.13 window_to_front: ^0.0.3 diff --git a/sdk/python/packages/flet/src/flet/core/file_picker.py b/sdk/python/packages/flet/src/flet/core/file_picker.py index 14e13ce48..569e78055 100644 --- a/sdk/python/packages/flet/src/flet/core/file_picker.py +++ b/sdk/python/packages/flet/src/flet/core/file_picker.py @@ -9,11 +9,6 @@ from flet.core.ref import Ref from flet.core.types import OptionalEventCallable -try: - from typing import Literal -except ImportError: - from typing_extensions import Literal - class FilePickerState(Enum): PICK_FILES = "pickFiles" diff --git a/sdk/python/packages/flet/src/flet/core/page.py b/sdk/python/packages/flet/src/flet/core/page.py index 3ec2476f0..d69084620 100644 --- a/sdk/python/packages/flet/src/flet/core/page.py +++ b/sdk/python/packages/flet/src/flet/core/page.py @@ -107,10 +107,9 @@ def page(cls) -> "Page": try: from flet.auth.authorization import Authorization from flet.auth.oauth_provider import OAuthProvider -except ImportError as e: +except ImportError: - class OAuthProvider: - ... + class OAuthProvider: ... class Authorization: def __init__( @@ -119,8 +118,7 @@ def __init__( fetch_user: bool, fetch_groups: bool, scope: Optional[List[str]] = None, - ): - ... + ): ... AT = TypeVar("AT", bound=Authorization) @@ -624,7 +622,9 @@ def convert_route_change_event(e): self._add_event_handler("route_change", self.__on_route_change.get_handler()) def convert_view_pop_event(e): - return ViewPopEvent(view=cast(View, self.get_control(e.data))) + # e.data contains route name + view = next((v for v in self.views if v.route == e.data), None) + return ViewPopEvent(view=view) if view in self.views else None self.__on_view_pop = EventHandler(convert_view_pop_event) self._add_event_handler("view_pop", self.__on_view_pop.get_handler()) diff --git a/sdk/python/packages/flet/src/flet/core/pubsub/pubsub_hub.py b/sdk/python/packages/flet/src/flet/core/pubsub/pubsub_hub.py index d9c8b1768..1a69e6498 100644 --- a/sdk/python/packages/flet/src/flet/core/pubsub/pubsub_hub.py +++ b/sdk/python/packages/flet/src/flet/core/pubsub/pubsub_hub.py @@ -22,34 +22,37 @@ def __init__( self.__executor = executor self.__lock = threading.Lock() if not is_pyodide() else NopeLock() self.__subscribers: Dict[ - str, Union[Callable, Callable[..., Awaitable[Any]]] + str, set[Union[Callable, Callable[..., Awaitable[Any]]]] ] = {} # key: session_id, value: handler self.__topic_subscribers: Dict[ - str, Dict[str, Union[Callable, Callable[..., Awaitable[Any]]]] + str, Dict[str, set[Union[Callable, Callable[..., Awaitable[Any]]]]] ] = {} # key: topic, value: dict[session_id, handler] self.__subscriber_topics: Dict[ - str, Dict[str, Union[Callable, Callable[..., Awaitable[Any]]]] + str, Dict[str, set[Union[Callable, Callable[..., Awaitable[Any]]]]] ] = {} # key: session_id, value: dict[topic, handler] def send_all(self, message: Any): logger.debug(f"pubsub.send_all({message})") with self.__lock: - for handler in self.__subscribers.values(): - self.__send(handler, [message]) + for handlers in self.__subscribers.values(): + for handler in handlers: + self.__send(handler, [message]) def send_all_on_topic(self, topic: str, message: Any): logger.debug(f"pubsub.send_all_on_topic({topic}, {message})") with self.__lock: if topic in self.__topic_subscribers: - for handler in self.__topic_subscribers[topic].values(): - self.__send(handler, [topic, message]) + for handlers in self.__topic_subscribers[topic].values(): + for handler in handlers: + self.__send(handler, [topic, message]) def send_others(self, except_session_id: str, message: Any): logger.debug(f"pubsub.send_others({except_session_id}, {message})") with self.__lock: - for session_id, handler in self.__subscribers.items(): + for session_id, handlers in self.__subscribers.items(): if except_session_id != session_id: - self.__send(handler, [message]) + for handler in handlers: + self.__send(handler, [message]) def send_others_on_topic(self, except_session_id: str, topic: str, message: Any): logger.debug( @@ -57,14 +60,19 @@ def send_others_on_topic(self, except_session_id: str, topic: str, message: Any) ) with self.__lock: if topic in self.__topic_subscribers: - for session_id, handler in self.__topic_subscribers[topic].items(): + for session_id, handlers in self.__topic_subscribers[topic].items(): if except_session_id != session_id: - self.__send(handler, [topic, message]) + for handler in handlers: + self.__send(handler, [topic, message]) def subscribe(self, session_id: str, handler: Callable): logger.debug(f"pubsub.subscribe({session_id})") with self.__lock: - self.__subscribers[session_id] = handler + handlers = self.__subscribers.get(session_id) + if handlers is None: + handlers = set() + self.__subscribers[session_id] = handlers + handlers.add(handler) def subscribe_topic( self, @@ -86,12 +94,20 @@ def __subscribe_topic( if topic_subscribers is None: topic_subscribers = {} self.__topic_subscribers[topic] = topic_subscribers - topic_subscribers[session_id] = handler + handlers = topic_subscribers.get(session_id) + if handlers is None: + handlers = set() + topic_subscribers[session_id] = handlers + handlers.add(handler) subscriber_topics = self.__subscriber_topics.get(session_id) if subscriber_topics is None: subscriber_topics = {} self.__subscriber_topics[session_id] = subscriber_topics - subscriber_topics[topic] = handler + handlers = subscriber_topics.get(topic) + if handlers is None: + handlers = set() + subscriber_topics[topic] = handlers + handlers.add(handler) def unsubscribe(self, session_id: str): logger.debug(f"pubsub.unsubscribe({session_id})")