From 150266566a1a963f2fab8ced23543174a39214c3 Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Wed, 30 Oct 2024 19:27:17 +0200 Subject: [PATCH 01/10] update 9 files --- .../flet/lib/src/controls/create_control.dart | 10 + .../flet/lib/src/controls/layout_builder.dart | 89 ++++++++ sdk/python/packages/flet/src/flet/__init__.py | 1 + .../flet/src/flet/core/layout_builder.py | 192 ++++++++++++++++++ 4 files changed, 292 insertions(+) create mode 100644 packages/flet/lib/src/controls/layout_builder.dart create mode 100644 sdk/python/packages/flet/src/flet/core/layout_builder.py diff --git a/packages/flet/lib/src/controls/create_control.dart b/packages/flet/lib/src/controls/create_control.dart index d90cb502f..8a923e4b7 100644 --- a/packages/flet/lib/src/controls/create_control.dart +++ b/packages/flet/lib/src/controls/create_control.dart @@ -116,6 +116,7 @@ import 'time_picker.dart'; import 'transparent_pointer.dart'; import 'vertical_divider.dart'; import 'window_drag_area.dart'; +import 'layout_builder.dart'; Widget createControl(Control? parent, String id, bool parentDisabled, {Widget? nextChild, bool? parentAdaptive}) { @@ -995,6 +996,15 @@ Widget createWidget( parentDisabled: parentDisabled, parentAdaptive: parentAdaptive, backend: backend); + case "layoutbuilder": + return LayoutBuilderControl( + key: key, + parent: parent, + control: controlView.control, + children: controlView.children, + parentDisabled: parentDisabled, + parentAdaptive: parentAdaptive, + backend: backend); default: return ErrorControl("Unknown control: ${controlView.control.type}"); } diff --git a/packages/flet/lib/src/controls/layout_builder.dart b/packages/flet/lib/src/controls/layout_builder.dart new file mode 100644 index 000000000..663fc77e4 --- /dev/null +++ b/packages/flet/lib/src/controls/layout_builder.dart @@ -0,0 +1,89 @@ +import 'package:flutter/widgets.dart'; + +import '../models/control.dart'; +import '../utils/alignment.dart'; +import '../utils/others.dart'; +import 'create_control.dart'; +import '../flet_control_backend.dart'; +import 'flet_store_mixin.dart'; + + + + + + + +class LayoutBuilderControl extends StatefulWidget { + final Control? parent; + final Control control; + final bool parentDisabled; + final bool? parentAdaptive; + final List children; + final FletControlBackend backend; + + + const LayoutBuilderControl({ + super.key, + this.parent, + required this.control, + required this.children, + required this.parentDisabled, + required this.parentAdaptive, + required this.backend + + }); + @override + State createState() => _LayoutBuilderControlState(); +} + +class _LayoutBuilderControlState extends State + with FletStoreMixin { + + + void updateAttributes(double width,double height) { + widget.backend.updateControlState(widget.control.id, {"widthLayout": width.toString()}); + widget.backend.updateControlState(widget.control.id, {"heightLayout": height.toString()}); + } + + + @override + Widget build(BuildContext context) { + debugPrint("Stack with layout builder build: ${widget.control.id}"); + bool disabled = widget.control.isDisabled || widget.parentDisabled; + bool? adaptive = widget.control.attrBool("adaptive") ?? widget.parentAdaptive; + + + var contentCtrls = + widget.children.where((c) => c.name == "content" && c.isVisible); + + + + Widget? child = contentCtrls.isNotEmpty + ? createControl(widget.control, contentCtrls.first.id, disabled, + parentAdaptive: adaptive) + : null; + + + return constrainedControl( + context, + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + double containerWidth = constraints.maxWidth; + double containerHeight = constraints.maxHeight; + + updateAttributes(containerWidth, containerHeight); + + debugPrint("LayoutBuilder dimensions: Width: $containerWidth, Height: $containerHeight"); + + return Container( + clipBehavior: Clip.none, + alignment: parseAlignment(widget.control, "alignment") ?? AlignmentDirectional.topStart, + child: child, + ); + }, + ), + widget.parent, + widget.control, + ); + } +} diff --git a/sdk/python/packages/flet/src/flet/__init__.py b/sdk/python/packages/flet/src/flet/__init__.py index 1c10714b9..2e3b9d9a1 100644 --- a/sdk/python/packages/flet/src/flet/__init__.py +++ b/sdk/python/packages/flet/src/flet/__init__.py @@ -306,6 +306,7 @@ from flet.core.slider import Slider, SliderInteraction from flet.core.snack_bar import DismissDirection, SnackBar, SnackBarBehavior from flet.core.stack import Stack, StackFit +from flet.core.layout_builder import LayoutBuilder from flet.core.submenu_button import SubmenuButton from flet.core.switch import Switch from flet.core.tabs import Tab, Tabs diff --git a/sdk/python/packages/flet/src/flet/core/layout_builder.py b/sdk/python/packages/flet/src/flet/core/layout_builder.py new file mode 100644 index 000000000..87d2ff91a --- /dev/null +++ b/sdk/python/packages/flet/src/flet/core/layout_builder.py @@ -0,0 +1,192 @@ +from enum import Enum +from typing import Any, List, Optional, Sequence, Union + +from flet.core.adaptive_control import AdaptiveControl +from flet.core.alignment import Alignment +from flet.core.animation import AnimationValue +from flet.core.constrained_control import ConstrainedControl +from flet.core.control import Control, OptionalNumber +from flet.core.ref import Ref +from flet.core.types import ( + ClipBehavior, + OffsetValue, + OptionalControlEventCallable, + ResponsiveNumber, + RotateValue, + ScaleValue, +) + + +class StackFit(Enum): + LOOSE = "loose" + EXPAND = "expand" + PASS_THROUGH = "passThrough" + + +class LayoutBuilder(ConstrainedControl, AdaptiveControl): + """ + A control that positions its children on top of each other. + + This control is useful if you want to overlap several children in a simple way, for example having some text and an image, overlaid with a gradient and a button attached to the bottom. + + Stack is also useful if you want to implement implicit animations (https://flet.dev/docs/guides/python/animations/) that require knowing absolute position of a target value. + + Example: + + ``` + import flet as ft + + def main(page: ft.Page): + st = ft.Stack( + controls=[ + ft.Image( + src=f"https://picsum.photos/300/300", + width=300, + height=300, + fit=ft.ImageFit.CONTAIN, + ), + ft.Row( + controls=[ + ft.Text( + "Image title", + color="white", + size=40, + weight="bold", + opacity=0.5, + ) + ], + alignment=ft.MainAxisAlignment.CENTER, + ), + ], + width=300, + height=300, + ) + + page.add(st) + + ft.app(target=main) + ``` + + ----- + + Online docs: https://flet.dev/docs/controls/stack + """ + + def __init__( + self, + content: Optional[Control] = None, + clip_behavior: Optional[ClipBehavior] = None, + alignment: Optional[Alignment] = None, + fit: Optional[StackFit] = None, + expand: Union[None, bool, int] = None, + # + # ConstrainedControl and AdaptiveControl + # + ref: Optional[Ref] = None, + key: Optional[str] = None, + visible: Optional[bool] = None, + disabled: Optional[bool] = None, + data: Any = None, + adaptive: Optional[bool] = None, + ): + ConstrainedControl.__init__( + self, + ref=ref, + key=key, + width=None, + height=None, + expand=expand, + visible=visible, + disabled=disabled, + data=data, + ) + + AdaptiveControl.__init__(self, adaptive=adaptive) + + self.content = content + self.clip_behavior = clip_behavior + self.alignment = alignment + self.fit = fit + + def _get_control_name(self): + return "layoutbuilder" + + def _get_children(self): + children = [] + if self.__content is not None: + self.__content._set_attr_internal("n", "content") + children.append(self.__content) + return children + + def before_update(self): + super().before_update() + self._set_attr_json("alignment", self.__alignment) + + # # controls + # @property + # def controls(self): + # return self.__controls + + # @controls.setter + # def controls(self, value: Optional[Sequence[Control]]): + # self.__controls = list(value) if value is not None else [] + + + # content + @property + def content(self) -> Optional[Control]: + return self.__content + + @content.setter + def content(self, value: Optional[Control]): + self.__content = value + + # clip_behavior + @property + def clip_behavior(self) -> Optional[ClipBehavior]: + return self.__clip_behavior + + @clip_behavior.setter + def clip_behavior(self, value: Optional[ClipBehavior]): + self.__clip_behavior = value + self._set_enum_attr("clipBehavior", value, ClipBehavior) + + # alignment + @property + def alignment(self) -> Optional[Alignment]: + return self.__alignment + + @alignment.setter + def alignment(self, value: Optional[Alignment]): + self.__alignment = value + + # # fit + # @property + # def fit(self) -> Optional[StackFit]: + # return self.__fit + + # @fit.setter + # def fit(self, value: Optional[StackFit]): + # self.__fit = value + # self._set_enum_attr("fit", value, StackFit) + + # @property + # def layout_size(self) -> Optional[str]: + # width = self._get_attr("widthLayout") + # height = self._get_attr("heightLayout") + # print(width,height) + + def __convert_to_float(self,value): + v = None + try: + v = float(value) + except: + pass + return v + + + def get_size(self) -> Union[float,None]: + width = self.__convert_to_float(self._get_attr("widthLayout")) + height = self.__convert_to_float(self._get_attr("heightLayout")) + return width,height + \ No newline at end of file From f47f9b1eb2563acc69c25f41d8e422b3ca1778f3 Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Thu, 31 Oct 2024 01:09:06 +0200 Subject: [PATCH 02/10] update layout_builder.dart and layout_builder.py --- .../flet/lib/src/controls/layout_builder.dart | 12 +++- .../flet/src/flet/core/layout_builder.py | 68 +++++++++++-------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/packages/flet/lib/src/controls/layout_builder.dart b/packages/flet/lib/src/controls/layout_builder.dart index 663fc77e4..d258c710d 100644 --- a/packages/flet/lib/src/controls/layout_builder.dart +++ b/packages/flet/lib/src/controls/layout_builder.dart @@ -38,12 +38,15 @@ class LayoutBuilderControl extends StatefulWidget { class _LayoutBuilderControlState extends State with FletStoreMixin { - + bool _hasChanged = false; void updateAttributes(double width,double height) { widget.backend.updateControlState(widget.control.id, {"widthLayout": width.toString()}); widget.backend.updateControlState(widget.control.id, {"heightLayout": height.toString()}); } + void onChange(double width,double height){ + widget.backend.triggerControlEvent(widget.control.id, "change", "$height $width"); + } @override @@ -51,6 +54,7 @@ class _LayoutBuilderControlState extends State debugPrint("Stack with layout builder build: ${widget.control.id}"); bool disabled = widget.control.isDisabled || widget.parentDisabled; bool? adaptive = widget.control.attrBool("adaptive") ?? widget.parentAdaptive; + var contentCtrls = @@ -73,6 +77,12 @@ class _LayoutBuilderControlState extends State updateAttributes(containerWidth, containerHeight); + if (_hasChanged){ //avoid first trigger after adding widget + onChange(containerWidth, containerHeight); + } else { + _hasChanged = true; + } + debugPrint("LayoutBuilder dimensions: Width: $containerWidth, Height: $containerHeight"); return Container( diff --git a/sdk/python/packages/flet/src/flet/core/layout_builder.py b/sdk/python/packages/flet/src/flet/core/layout_builder.py index 87d2ff91a..72314089a 100644 --- a/sdk/python/packages/flet/src/flet/core/layout_builder.py +++ b/sdk/python/packages/flet/src/flet/core/layout_builder.py @@ -79,6 +79,7 @@ def __init__( alignment: Optional[Alignment] = None, fit: Optional[StackFit] = None, expand: Union[None, bool, int] = None, + on_change: OptionalControlEventCallable = None, # # ConstrainedControl and AdaptiveControl # @@ -93,8 +94,6 @@ def __init__( self, ref=ref, key=key, - width=None, - height=None, expand=expand, visible=visible, disabled=disabled, @@ -106,8 +105,29 @@ def __init__( self.content = content self.clip_behavior = clip_behavior self.alignment = alignment + self.__on_change_callback = on_change + self.on_change = self.__on_change self.fit = fit + self.__width_layout = None + self.__height_layout = None + + def __on_change(self,e): + data = e.data + data = data.split(" ") + width = data[0] + height = data[1] + if height!=self.__height_layout or width!=self.__width_layout: + self.__width_layout = width + self.__height_layout = height + print(width,height) + + + + + + + def _get_control_name(self): return "layoutbuilder" @@ -121,17 +141,7 @@ def _get_children(self): def before_update(self): super().before_update() self._set_attr_json("alignment", self.__alignment) - - # # controls - # @property - # def controls(self): - # return self.__controls - - # @controls.setter - # def controls(self, value: Optional[Sequence[Control]]): - # self.__controls = list(value) if value is not None else [] - - + # content @property def content(self) -> Optional[Control]: @@ -159,22 +169,15 @@ def alignment(self) -> Optional[Alignment]: @alignment.setter def alignment(self, value: Optional[Alignment]): self.__alignment = value - - # # fit - # @property - # def fit(self) -> Optional[StackFit]: - # return self.__fit - - # @fit.setter - # def fit(self, value: Optional[StackFit]): - # self.__fit = value - # self._set_enum_attr("fit", value, StackFit) - # @property - # def layout_size(self) -> Optional[str]: - # width = self._get_attr("widthLayout") - # height = self._get_attr("heightLayout") - # print(width,height) + # on_change + @property + def on_change(self) -> OptionalControlEventCallable: + return self._get_event_handler("change") + + @on_change.setter + def on_change(self, handler: OptionalControlEventCallable): + self._add_event_handler("change", handler) def __convert_to_float(self,value): v = None @@ -184,9 +187,14 @@ def __convert_to_float(self,value): pass return v + def get_width(self) -> Union[float,None]: + return self.__convert_to_float(self._get_attr("widthLayout")) + + def get_height(self) -> Union[float,None]: + return self.__convert_to_float(self._get_attr("heightLayout")) def get_size(self) -> Union[float,None]: - width = self.__convert_to_float(self._get_attr("widthLayout")) - height = self.__convert_to_float(self._get_attr("heightLayout")) + width = self.get_width() + height = self.get_height() return width,height \ No newline at end of file From 9b8937fea77fee4c12f24ef8fa6251e7cab390ff Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Fri, 22 Nov 2024 03:51:32 +0200 Subject: [PATCH 03/10] update 9 files --- .../flet/lib/src/controls/layout_builder.dart | 96 ++++++++------ .../flet/src/flet/core/layout_builder.py | 119 +++++------------- 2 files changed, 91 insertions(+), 124 deletions(-) diff --git a/packages/flet/lib/src/controls/layout_builder.dart b/packages/flet/lib/src/controls/layout_builder.dart index d258c710d..f453a5d3b 100644 --- a/packages/flet/lib/src/controls/layout_builder.dart +++ b/packages/flet/lib/src/controls/layout_builder.dart @@ -1,17 +1,10 @@ import 'package:flutter/widgets.dart'; - import '../models/control.dart'; import '../utils/alignment.dart'; -import '../utils/others.dart'; import 'create_control.dart'; import '../flet_control_backend.dart'; import 'flet_store_mixin.dart'; - - - - - - +import 'dart:convert'; class LayoutBuilderControl extends StatefulWidget { final Control? parent; @@ -21,7 +14,6 @@ class LayoutBuilderControl extends StatefulWidget { final List children; final FletControlBackend backend; - const LayoutBuilderControl({ super.key, this.parent, @@ -29,65 +21,95 @@ class LayoutBuilderControl extends StatefulWidget { required this.children, required this.parentDisabled, required this.parentAdaptive, - required this.backend - + required this.backend, }); + @override State createState() => _LayoutBuilderControlState(); } class _LayoutBuilderControlState extends State with FletStoreMixin { - bool _hasChanged = false; + final GlobalKey _widgetKey = GlobalKey(); + Size? _lastSize; + bool _hasInitialized = false; + bool _updateOnBuild = false; + double xPosition = 0.0; + double yPosition = 0.0; - void updateAttributes(double width,double height) { - widget.backend.updateControlState(widget.control.id, {"widthLayout": width.toString()}); - widget.backend.updateControlState(widget.control.id, {"heightLayout": height.toString()}); + @override + void initState() { + super.initState(); + _updateOnBuild = widget.control.attrBool("update_on_build") ?? false; + debugPrint("UPDATEEEEE ON BUUUUIIILLDD: $_updateOnBuild"); } - void onChange(double width,double height){ - widget.backend.triggerControlEvent(widget.control.id, "change", "$height $width"); - } + void onChange(double width, double height) { + widget.backend.triggerControlEvent( + widget.control.id, + "layout_change", + jsonEncode({ + "height": height, + "width": width, + "x_position": xPosition, + "y_position": yPosition, + }), + ); + } @override Widget build(BuildContext context) { debugPrint("Stack with layout builder build: ${widget.control.id}"); + bool disabled = widget.control.isDisabled || widget.parentDisabled; - bool? adaptive = widget.control.attrBool("adaptive") ?? widget.parentAdaptive; - + bool? adaptive = + widget.control.attrBool("adaptive") ?? widget.parentAdaptive; - var contentCtrls = widget.children.where((c) => c.name == "content" && c.isVisible); - - Widget? child = contentCtrls.isNotEmpty ? createControl(widget.control, contentCtrls.first.id, disabled, parentAdaptive: adaptive) : null; - return constrainedControl( context, LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - double containerWidth = constraints.maxWidth; - double containerHeight = constraints.maxHeight; - - updateAttributes(containerWidth, containerHeight); - - if (_hasChanged){ //avoid first trigger after adding widget - onChange(containerWidth, containerHeight); - } else { - _hasChanged = true; - } + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_widgetKey.currentContext != null) { + final RenderBox box = + _widgetKey.currentContext!.findRenderObject() as RenderBox; + final Offset position = box.localToGlobal(Offset.zero); + xPosition = position.dx; + yPosition = position.dy; + + debugPrint("Widget position: x: $xPosition, y: $yPosition"); + } + + final Size currentSize = + Size(constraints.maxWidth, constraints.maxHeight); + if (_hasInitialized == false && _updateOnBuild == true) { + onChange(constraints.maxWidth, constraints.maxHeight); + debugPrint("ON CHANGE FIRST UPDATE!!!!!!!!!"); + } + if (_hasInitialized == true && _lastSize != currentSize) { + onChange(constraints.maxWidth, constraints.maxHeight); + debugPrint("ON CHANGE SECONDDDDDDDDDD UPDATE!!!!!!!!!"); + } + _hasInitialized = true; + + _lastSize = currentSize; + debugPrint( + "LayoutBuilder dimensions: Width: ${constraints.maxWidth}, Height: ${constraints.maxHeight}"); + }); - debugPrint("LayoutBuilder dimensions: Width: $containerWidth, Height: $containerHeight"); - return Container( + key: _widgetKey, clipBehavior: Clip.none, - alignment: parseAlignment(widget.control, "alignment") ?? AlignmentDirectional.topStart, + alignment: parseAlignment(widget.control, "alignment") ?? + AlignmentDirectional.topStart, child: child, ); }, diff --git a/sdk/python/packages/flet/src/flet/core/layout_builder.py b/sdk/python/packages/flet/src/flet/core/layout_builder.py index 72314089a..0835e7796 100644 --- a/sdk/python/packages/flet/src/flet/core/layout_builder.py +++ b/sdk/python/packages/flet/src/flet/core/layout_builder.py @@ -1,10 +1,12 @@ from enum import Enum +import json from typing import Any, List, Optional, Sequence, Union from flet.core.adaptive_control import AdaptiveControl from flet.core.alignment import Alignment from flet.core.animation import AnimationValue from flet.core.constrained_control import ConstrainedControl +from flet.core.control_event import ControlEvent from flet.core.control import Control, OptionalNumber from flet.core.ref import Ref from flet.core.types import ( @@ -22,56 +24,17 @@ class StackFit(Enum): EXPAND = "expand" PASS_THROUGH = "passThrough" +class LayoutDimensions(ControlEvent): + def __init__(self, e: ControlEvent): + super().__init__(e.target, e.name, e.data, e.control, e.page) + d = json.loads(e.data) + self.width: float = d.get("width") + self.height: float = d.get("height") + self.x_pos: float = d.get("x_position") + self.y_pos: float = d.get("y_position") + class LayoutBuilder(ConstrainedControl, AdaptiveControl): - """ - A control that positions its children on top of each other. - - This control is useful if you want to overlap several children in a simple way, for example having some text and an image, overlaid with a gradient and a button attached to the bottom. - - Stack is also useful if you want to implement implicit animations (https://flet.dev/docs/guides/python/animations/) that require knowing absolute position of a target value. - - Example: - - ``` - import flet as ft - - def main(page: ft.Page): - st = ft.Stack( - controls=[ - ft.Image( - src=f"https://picsum.photos/300/300", - width=300, - height=300, - fit=ft.ImageFit.CONTAIN, - ), - ft.Row( - controls=[ - ft.Text( - "Image title", - color="white", - size=40, - weight="bold", - opacity=0.5, - ) - ], - alignment=ft.MainAxisAlignment.CENTER, - ), - ], - width=300, - height=300, - ) - - page.add(st) - - ft.app(target=main) - ``` - - ----- - - Online docs: https://flet.dev/docs/controls/stack - """ - def __init__( self, content: Optional[Control] = None, @@ -89,6 +52,7 @@ def __init__( disabled: Optional[bool] = None, data: Any = None, adaptive: Optional[bool] = None, + update_size_on_init: Optional[bool] = True ): ConstrainedControl.__init__( self, @@ -105,28 +69,26 @@ def __init__( self.content = content self.clip_behavior = clip_behavior self.alignment = alignment - self.__on_change_callback = on_change - self.on_change = self.__on_change self.fit = fit - self.__width_layout = None - self.__height_layout = None - - def __on_change(self,e): - data = e.data - data = data.split(" ") - width = data[0] - height = data[1] - if height!=self.__height_layout or width!=self.__width_layout: - self.__width_layout = width - self.__height_layout = height - print(width,height) - - - + self.__on_change_callback = on_change + self.width_layout: float = None + self.height_layout: float = None + self.__update_size_on_init = update_size_on_init + self.on_change = self.__on_change + + + def __on_change(self,e): + e = LayoutDimensions(e) + self.width = e.width + self.height = e.height + self.x = e.x_pos + self.y = e.y_pos + if self.__on_change_callback: + self.__on_change_callback(e) def _get_control_name(self): return "layoutbuilder" @@ -140,6 +102,8 @@ def _get_children(self): def before_update(self): super().before_update() + if self.__update_size_on_init==True: + self._set_attr_json("update_on_build", self.__update_size_on_init) self._set_attr_json("alignment", self.__alignment) # content @@ -173,28 +137,9 @@ def alignment(self, value: Optional[Alignment]): # on_change @property def on_change(self) -> OptionalControlEventCallable: - return self._get_event_handler("change") + return self._get_event_handler("layout_change") @on_change.setter def on_change(self, handler: OptionalControlEventCallable): - self._add_event_handler("change", handler) - - def __convert_to_float(self,value): - v = None - try: - v = float(value) - except: - pass - return v - - def get_width(self) -> Union[float,None]: - return self.__convert_to_float(self._get_attr("widthLayout")) - - def get_height(self) -> Union[float,None]: - return self.__convert_to_float(self._get_attr("heightLayout")) - - def get_size(self) -> Union[float,None]: - width = self.get_width() - height = self.get_height() - return width,height - \ No newline at end of file + self._add_event_handler("layout_change", handler) + From 14d73b3f64f917c29b8e495b1e1f8ab7bdeaa06e Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Wed, 27 Nov 2024 07:52:03 +0200 Subject: [PATCH 04/10] update 15 files --- client/lib/main.dart | 3 + .../flutter/generated_plugin_registrant.cc | 8 + client/linux/flutter/generated_plugins.cmake | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 4 + client/pubspec.lock | 31 +++ client/pubspec.yaml | 2 + .../flutter/generated_plugin_registrant.cc | 6 + .../windows/flutter/generated_plugins.cmake | 2 + .../flet/lib/src/controls/layout_builder.dart | 11 +- packages/flet/lib/src/controls/page.dart | 12 + .../flet/lib/src/utils/background_blur.dart | 49 ++++ packages/flet/lib/src/utils/desktop.dart | 10 +- packages/flet/pubspec.yaml | 1 + packages/flet_dropzone/.gitignore | 31 +++ packages/flet_dropzone/.metadata | 10 + packages/flet_dropzone/CHANGELOG.md | 27 ++ packages/flet_dropzone/LICENSE | 201 +++++++++++++++ packages/flet_dropzone/README.md | 3 + packages/flet_dropzone/analysis_options.yaml | 4 + packages/flet_dropzone/lib/flet_dropzone.dart | 3 + .../flet_dropzone/lib/src/create_control.dart | 22 ++ packages/flet_dropzone/lib/src/dropzone.dart | 128 ++++++++++ packages/flet_dropzone/pubspec.yaml | 24 ++ sdk/python/packages/flet/src/flet/__init__.py | 2 + .../packages/flet/src/flet/core/bgblur.py | 6 + .../packages/flet/src/flet/core/drop_zone.py | 237 ++++++++++++++++++ .../flet/src/flet/core/layout_builder.py | 28 ++- .../packages/flet/src/flet/core/page.py | 9 + 28 files changed, 860 insertions(+), 16 deletions(-) create mode 100644 packages/flet/lib/src/utils/background_blur.dart create mode 100644 packages/flet_dropzone/.gitignore create mode 100644 packages/flet_dropzone/.metadata create mode 100644 packages/flet_dropzone/CHANGELOG.md create mode 100644 packages/flet_dropzone/LICENSE create mode 100644 packages/flet_dropzone/README.md create mode 100644 packages/flet_dropzone/analysis_options.yaml create mode 100644 packages/flet_dropzone/lib/flet_dropzone.dart create mode 100644 packages/flet_dropzone/lib/src/create_control.dart create mode 100644 packages/flet_dropzone/lib/src/dropzone.dart create mode 100644 packages/flet_dropzone/pubspec.yaml create mode 100644 sdk/python/packages/flet/src/flet/core/bgblur.py create mode 100644 sdk/python/packages/flet/src/flet/core/drop_zone.py diff --git a/client/lib/main.dart b/client/lib/main.dart index 6ca220d23..60cd36487 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -13,6 +13,7 @@ import 'package:flet_map/flet_map.dart' as flet_map; import 'package:flet_permission_handler/flet_permission_handler.dart' as flet_permission_handler; import 'package:flet_rive/flet_rive.dart' as flet_rive; +import 'package:flet_dropzone/flet_dropzone.dart' as flet_dropzone; // --FAT_CLIENT_START-- import 'package:flet_video/flet_video.dart' as flet_video; // --FAT_CLIENT_END-- @@ -45,6 +46,7 @@ void main([List? args]) async { flet_rive.ensureInitialized(); flet_webview.ensureInitialized(); flet_flashlight.ensureInitialized(); + flet_dropzone.ensureInitialized(); var pageUrl = Uri.base.toString(); var assetsDir = ""; @@ -114,6 +116,7 @@ void main([List? args]) async { flet_rive.createControl, flet_webview.createControl, flet_flashlight.createControl, + flet_dropzone.createControl, ], )); } diff --git a/client/linux/flutter/generated_plugin_registrant.cc b/client/linux/flutter/generated_plugin_registrant.cc index 97a3db8e3..ab1eaf210 100644 --- a/client/linux/flutter/generated_plugin_registrant.cc +++ b/client/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,8 @@ #include "generated_plugin_registrant.h" #include +#include +#include #include #include #include @@ -19,6 +21,12 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); + g_autoptr(FlPluginRegistrar) desktop_drop_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); + desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); + g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin"); + flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar); g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); diff --git a/client/linux/flutter/generated_plugins.cmake b/client/linux/flutter/generated_plugins.cmake index 05e3ad3f0..365028247 100644 --- a/client/linux/flutter/generated_plugins.cmake +++ b/client/linux/flutter/generated_plugins.cmake @@ -4,6 +4,8 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux + desktop_drop + flutter_acrylic media_kit_libs_linux media_kit_video record_linux diff --git a/client/macos/Flutter/GeneratedPluginRegistrant.swift b/client/macos/Flutter/GeneratedPluginRegistrant.swift index a9516dbb7..ccd0a38df 100644 --- a/client/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/client/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,9 @@ import FlutterMacOS import Foundation import audioplayers_darwin +import desktop_drop import geolocator_apple +import macos_window_utils import media_kit_libs_macos_video import media_kit_video import package_info_plus @@ -23,7 +25,9 @@ import window_to_front func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) + DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) diff --git a/client/pubspec.lock b/client/pubspec.lock index 6d9e58d84..032652b6b 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -169,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" + desktop_drop: + dependency: transitive + description: + name: desktop_drop + sha256: "03abf1c0443afdd1d65cf8fa589a2f01c67a11da56bbb06f6ea1de79d5628e94" + url: "https://pub.dev" + source: hosted + version: "0.5.0" dio: dependency: transitive description: @@ -262,6 +270,13 @@ packages: relative: true source: path version: "0.24.1" + flet_dropzone: + dependency: "direct main" + description: + path: "../packages/flet_dropzone" + relative: true + source: path + version: "0.24.1" flet_flashlight: dependency: "direct main" description: @@ -323,6 +338,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_acrylic: + dependency: transitive + description: + name: flutter_acrylic + sha256: b3996dbde5abf5823cc9ead4cf2e5267c3181f15585fe47ce4dc4472e7ec827a + url: "https://pub.dev" + source: hosted + version: "1.1.4" flutter_driver: dependency: transitive description: flutter @@ -617,6 +640,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + macos_window_utils: + dependency: transitive + description: + name: macos_window_utils + sha256: "3534f2af024f2f24112ca28789a44e6750083f8c0065414546c6593ee48a5009" + url: "https://pub.dev" + source: hosted + version: "1.6.1" markdown: dependency: transitive description: diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 33eecdd2d..91cb8f3cb 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -55,6 +55,8 @@ dependencies: path: ../packages/flet_webview flet_flashlight: path: ../packages/flet_flashlight + flet_dropzone: + path: ../packages/flet_dropzone url_strategy: ^0.2.0 cupertino_icons: ^1.0.6 diff --git a/client/windows/flutter/generated_plugin_registrant.cc b/client/windows/flutter/generated_plugin_registrant.cc index 3487fa00e..47df1f8f7 100644 --- a/client/windows/flutter/generated_plugin_registrant.cc +++ b/client/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,8 @@ #include "generated_plugin_registrant.h" #include +#include +#include #include #include #include @@ -22,6 +24,10 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { AudioplayersWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); + DesktopDropPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopDropPlugin")); + FlutterAcrylicPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterAcrylicPlugin")); GeolocatorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("GeolocatorWindows")); MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( diff --git a/client/windows/flutter/generated_plugins.cmake b/client/windows/flutter/generated_plugins.cmake index 70f93ecdb..730d00db1 100644 --- a/client/windows/flutter/generated_plugins.cmake +++ b/client/windows/flutter/generated_plugins.cmake @@ -4,6 +4,8 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows + desktop_drop + flutter_acrylic geolocator_windows media_kit_libs_windows_video media_kit_video diff --git a/packages/flet/lib/src/controls/layout_builder.dart b/packages/flet/lib/src/controls/layout_builder.dart index f453a5d3b..1b314a453 100644 --- a/packages/flet/lib/src/controls/layout_builder.dart +++ b/packages/flet/lib/src/controls/layout_builder.dart @@ -27,7 +27,6 @@ class LayoutBuilderControl extends StatefulWidget { @override State createState() => _LayoutBuilderControlState(); } - class _LayoutBuilderControlState extends State with FletStoreMixin { final GlobalKey _widgetKey = GlobalKey(); @@ -41,10 +40,16 @@ class _LayoutBuilderControlState extends State void initState() { super.initState(); _updateOnBuild = widget.control.attrBool("update_on_build") ?? false; - debugPrint("UPDATEEEEE ON BUUUUIIILLDD: $_updateOnBuild"); } void onChange(double width, double height) { + + widget.backend.updateControlState(widget.control.id, {"layoutWidth": width.toString()}); + widget.backend.updateControlState(widget.control.id, {"layoutheight": height.toString()}); + + widget.backend.updateControlState(widget.control.id, {"xPos": width.toString()}); + widget.backend.updateControlState(widget.control.id, {"yPos": height.toString()}); + widget.backend.triggerControlEvent( widget.control.id, "layout_change", @@ -92,11 +97,9 @@ class _LayoutBuilderControlState extends State Size(constraints.maxWidth, constraints.maxHeight); if (_hasInitialized == false && _updateOnBuild == true) { onChange(constraints.maxWidth, constraints.maxHeight); - debugPrint("ON CHANGE FIRST UPDATE!!!!!!!!!"); } if (_hasInitialized == true && _lastSize != currentSize) { onChange(constraints.maxWidth, constraints.maxHeight); - debugPrint("ON CHANGE SECONDDDDDDDDDD UPDATE!!!!!!!!!"); } _hasInitialized = true; diff --git a/packages/flet/lib/src/controls/page.dart b/packages/flet/lib/src/controls/page.dart index 4d423e1c5..d720baf63 100644 --- a/packages/flet/lib/src/controls/page.dart +++ b/packages/flet/lib/src/controls/page.dart @@ -134,6 +134,10 @@ class _PageControlState extends State with FletStoreMixin { ThemeMode? _themeMode; Map? _localeConfiguration; String? _windowTitle; + + + String? _windowBgblur; + Color? _windowBgcolor; double? _windowWidth; double? _windowHeight; @@ -300,6 +304,7 @@ class _PageControlState extends State with FletStoreMixin { // window params var windowTitle = widget.control.attrString("title", "")!; + var windowBgblur = widget.control.attrString("windowBgblur", "disabled")!; var windowBgcolor = widget.control.attrColor("windowBgcolor", context); var windowWidth = widget.control.attrDouble("windowWidth"); var windowHeight = widget.control.attrDouble("windowHeight"); @@ -351,6 +356,13 @@ class _PageControlState extends State with FletStoreMixin { setWindowBackgroundColor(windowBgcolor); _windowBgcolor = windowBgcolor; } + + //WindowBgblur + if (_windowBgblur != windowBgblur) { + setWindowBackgroundBlur( + windowBgblur); + _windowBgblur = windowBgblur; + } // window size if ((windowWidth != null || windowHeight != null) && diff --git a/packages/flet/lib/src/utils/background_blur.dart b/packages/flet/lib/src/utils/background_blur.dart new file mode 100644 index 000000000..145a4adb6 --- /dev/null +++ b/packages/flet/lib/src/utils/background_blur.dart @@ -0,0 +1,49 @@ + +import 'package:flutter/material.dart'; +import 'package:flutter_acrylic/flutter_acrylic.dart'; + +bool _isInitialized = false; + +Future initializeWindow() async { + WidgetsFlutterBinding.ensureInitialized(); + + if (!_isInitialized) { + await Window.initialize(); + _isInitialized = true; + } +} + +Future setWindowEffect(String effectName) async { + if (!_isInitialized) { + await initializeWindow(); + } + + WindowEffect effect; + + switch (effectName.toLowerCase()) { + case 'mica': + effect = WindowEffect.mica; + break; + case 'acrylic': + effect = WindowEffect.acrylic; + break; + case 'transparent': + effect = WindowEffect.transparent; + break; + case 'disabled': + effect = WindowEffect.disabled; + break; + case 'tabbed': + effect = WindowEffect.tabbed; + break; + default: + effect = WindowEffect.disabled; + break; + } +//Color.fromARGB(0, 255, 255, 255), + await Window.setEffect( + effect: effect, + color: Colors.transparent, + dark: true + ); +} diff --git a/packages/flet/lib/src/utils/desktop.dart b/packages/flet/lib/src/utils/desktop.dart index 5e2f21161..351ab7a83 100644 --- a/packages/flet/lib/src/utils/desktop.dart +++ b/packages/flet/lib/src/utils/desktop.dart @@ -2,8 +2,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:window_manager/window_manager.dart'; import 'package:window_to_front/window_to_front.dart'; - import '../models/window_media_data.dart'; +import 'background_blur.dart'; + Future setWindowTitle(String title) async { if (isDesktop()) { @@ -19,6 +20,13 @@ Future setWindowBackgroundColor(Color bgcolor) async { } } +Future setWindowBackgroundBlur(String bgblur) async { + if (isDesktop()) { + debugPrint("setWindowBackgroundBlur($bgblur)"); + await setWindowEffect(bgblur); + } +} + Future setWindowSize(double? width, double? height) async { if (isDesktop()) { debugPrint("setWindowSize($width, $height)"); diff --git a/packages/flet/pubspec.yaml b/packages/flet/pubspec.yaml index 3965cf4e2..37001e862 100644 --- a/packages/flet/pubspec.yaml +++ b/packages/flet/pubspec.yaml @@ -42,6 +42,7 @@ dependencies: path: ^1.8.2 js: ^0.6.5 fl_chart: ^0.68.0 + flutter_acrylic: ^1.1.4 dev_dependencies: flutter_test: diff --git a/packages/flet_dropzone/.gitignore b/packages/flet_dropzone/.gitignore new file mode 100644 index 000000000..e050eb512 --- /dev/null +++ b/packages/flet_dropzone/.gitignore @@ -0,0 +1,31 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ +.flutter-plugins +.flutter-plugins-dependencies \ No newline at end of file diff --git a/packages/flet_dropzone/.metadata b/packages/flet_dropzone/.metadata new file mode 100644 index 000000000..07d8623a3 --- /dev/null +++ b/packages/flet_dropzone/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2e9cb0aa71a386a91f73f7088d115c0d96654829" + channel: "stable" + +project_type: package diff --git a/packages/flet_dropzone/CHANGELOG.md b/packages/flet_dropzone/CHANGELOG.md new file mode 100644 index 000000000..623f8a879 --- /dev/null +++ b/packages/flet_dropzone/CHANGELOG.md @@ -0,0 +1,27 @@ +# 0.24.1 + +Version follows parent `flet` package. + +# 0.24.0 + +Version follows parent `flet` package. + +# 0.23.2 + +No changes in this release. Version bumped to follow parent `flet` package. + +# 0.23.1 + +No changes in this release. Version bumped to follow parent `flet` package. + +# 0.23.0 + +No changes in this release. Version bumped to follow parent `flet` package. + +# 0.22.1 + +No changes in this release. Version bumped to follow parent `flet` package. + +# 0.22.0 + +Initial release of the package. \ No newline at end of file diff --git a/packages/flet_dropzone/LICENSE b/packages/flet_dropzone/LICENSE new file mode 100644 index 000000000..f49a4e16e --- /dev/null +++ b/packages/flet_dropzone/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/packages/flet_dropzone/README.md b/packages/flet_dropzone/README.md new file mode 100644 index 000000000..b6a4d5d12 --- /dev/null +++ b/packages/flet_dropzone/README.md @@ -0,0 +1,3 @@ +# Flet `Drop Zone` control + +`Drop Zone` control to use in Flet apps. \ No newline at end of file diff --git a/packages/flet_dropzone/analysis_options.yaml b/packages/flet_dropzone/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/packages/flet_dropzone/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/flet_dropzone/lib/flet_dropzone.dart b/packages/flet_dropzone/lib/flet_dropzone.dart new file mode 100644 index 000000000..aab490a29 --- /dev/null +++ b/packages/flet_dropzone/lib/flet_dropzone.dart @@ -0,0 +1,3 @@ +library flet_dropzone; + +export "src/create_control.dart" show createControl, ensureInitialized; diff --git a/packages/flet_dropzone/lib/src/create_control.dart b/packages/flet_dropzone/lib/src/create_control.dart new file mode 100644 index 000000000..92a1b3103 --- /dev/null +++ b/packages/flet_dropzone/lib/src/create_control.dart @@ -0,0 +1,22 @@ +import 'package:flet/flet.dart'; + +import 'dropzone.dart'; + +CreateControlFactory createControl = (CreateControlArgs args) { + switch (args.control.type) { + case "dropzone": + return DropZoneControl( + parent: args.parent, + control: args.control, + children: args.children, + parentDisabled: args.parentDisabled, + parentAdaptive: args.parentAdaptive, + backend: args.backend); + default: + return null; + } +}; + +void ensureInitialized() { + // nothing to initialize +} diff --git a/packages/flet_dropzone/lib/src/dropzone.dart b/packages/flet_dropzone/lib/src/dropzone.dart new file mode 100644 index 000000000..bcd67958b --- /dev/null +++ b/packages/flet_dropzone/lib/src/dropzone.dart @@ -0,0 +1,128 @@ +import 'dart:convert'; + +import 'package:flet/flet.dart'; +import 'package:flutter/widgets.dart'; +import 'package:desktop_drop/desktop_drop.dart'; + +class DropZoneControl extends StatefulWidget { + final Control? parent; + final Control control; + final List children; + final bool parentDisabled; + final bool? parentAdaptive; + final FletControlBackend backend; + + const DropZoneControl( + {super.key, + required this.parent, + required this.control, + required this.children, + required this.parentDisabled, + required this.parentAdaptive, + required this.backend}); + + @override + State createState() => _DropZoneControlState(); +} + +class _DropZoneControlState extends State with FletStoreMixin { + + bool _dragging = false; + List _allowedFileTypes = []; + + List _droppedFiles = []; + + + @override + void initState() { + super.initState(); + _allowedFileTypes = widget.control.attrList("allowedFileTypes") ?? []; + } + + void onDragDone() { + widget.backend.triggerControlEvent( + widget.control.id, + "dropped", + jsonEncode({ + "files": _droppedFiles + }), + ); + } + + void onDragEntered() { + widget.backend.triggerControlEvent( + widget.control.id, + "entered", + "" + ); + } + + void onDragExited() { + widget.backend.triggerControlEvent( + widget.control.id, + "exited", + "" + ); + } + + + + + @override + Widget build(BuildContext context) { + debugPrint("DropZone build: ${widget.control.id} (${widget.control.hashCode})"); + bool disabled = widget.control.isDisabled || widget.parentDisabled; + + var contentCtrls = + widget.children.where((c) => c.name == "content" && c.isVisible); + + Widget child = contentCtrls.isNotEmpty + ? createControl(widget.control, contentCtrls.first.id, disabled) + : Container(); + + + return withPageArgs((context, pageArgs) { + Widget? dropZone; + + dropZone = DropTarget( + onDragEntered: (details) { + setState(() { + _dragging = true; + }); + onDragEntered(); + + }, + onDragExited: (details) { + setState(() { + _dragging = false; + }); + onDragExited(); + + }, + onDragDone: (details) { + setState(() { + _droppedFiles = details.files + .map((file) => file.path) + .where((filePath) { + if (_allowedFileTypes.isEmpty) return true; + + final extension = filePath.split('.').last.toLowerCase(); + return _allowedFileTypes.contains(extension); + }) + .toList(); + _dragging = false; + }); + if (_droppedFiles.isNotEmpty){ + onDragDone(); + } + + + }, + enable: !disabled, + child: child, + ); + + return constrainedControl(context, dropZone, widget.parent, widget.control); + }); + } +} diff --git a/packages/flet_dropzone/pubspec.yaml b/packages/flet_dropzone/pubspec.yaml new file mode 100644 index 000000000..4010c8cc9 --- /dev/null +++ b/packages/flet_dropzone/pubspec.yaml @@ -0,0 +1,24 @@ +name: flet_dropzone +description: Flet Drop Zone control +homepage: https://flet.dev +repository: https://github.com/flet-dev/flet/packages/flet_dropzone +version: 0.24.1 + +environment: + sdk: '>=3.2.3 <4.0.0' + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + collection: ^1.16.0 + desktop_drop: ^0.5.0 + + flet: + path: ../flet/ + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 \ No newline at end of file diff --git a/sdk/python/packages/flet/src/flet/__init__.py b/sdk/python/packages/flet/src/flet/__init__.py index 2e3b9d9a1..9693fafa6 100644 --- a/sdk/python/packages/flet/src/flet/__init__.py +++ b/sdk/python/packages/flet/src/flet/__init__.py @@ -14,6 +14,7 @@ padding, painting, transform, + bgblur, ) from flet.core.adaptive_control import AdaptiveControl from flet.core.alert_dialog import AlertDialog @@ -307,6 +308,7 @@ from flet.core.snack_bar import DismissDirection, SnackBar, SnackBarBehavior from flet.core.stack import Stack, StackFit from flet.core.layout_builder import LayoutBuilder +from flet.core.drop_zone import DropZone from flet.core.submenu_button import SubmenuButton from flet.core.switch import Switch from flet.core.tabs import Tab, Tabs diff --git a/sdk/python/packages/flet/src/flet/core/bgblur.py b/sdk/python/packages/flet/src/flet/core/bgblur.py new file mode 100644 index 000000000..93d3b6cff --- /dev/null +++ b/sdk/python/packages/flet/src/flet/core/bgblur.py @@ -0,0 +1,6 @@ +MICA = 'mica' +ACRYLIC = 'acrylic' +AERO = 'aero' +TABBED = 'tabbed' +DISABLED = 'disabled' +TRANSPARENT = 'transparent' \ No newline at end of file diff --git a/sdk/python/packages/flet/src/flet/core/drop_zone.py b/sdk/python/packages/flet/src/flet/core/drop_zone.py new file mode 100644 index 000000000..f64d541d5 --- /dev/null +++ b/sdk/python/packages/flet/src/flet/core/drop_zone.py @@ -0,0 +1,237 @@ +import asyncio +from enum import Enum +import json +from typing import Any, List, Optional, Sequence, Union + +from flet.core.adaptive_control import AdaptiveControl +from flet.core.alignment import Alignment +from flet.core.animation import AnimationValue +from flet.core.tooltip import TooltipValue +from flet.core.badge import BadgeValue + + +from flet.core.constrained_control import ConstrainedControl +from flet.core.control_event import ControlEvent +from flet.core.control import Control, OptionalNumber +from flet.core.ref import Ref +from flet.core.types import ( + ClipBehavior, + OffsetValue, + OptionalControlEventCallable, + ResponsiveNumber, + RotateValue, + ScaleValue, +) + + +class StackFit(Enum): + LOOSE = "loose" + EXPAND = "expand" + PASS_THROUGH = "passThrough" + +class ListFiles(ControlEvent): + def __init__(self, e: ControlEvent): + super().__init__(e.target, e.name, e.data, e.control, e.page) + d = json.loads(e.data) + self.files: float = d.get("files") + +class EventHandler: + def __init__(self, result_converter=None) -> None: + self.__result_converter = result_converter + self.handler: OptionalControlEventCallable = None + + def get_handler(self): + async def fn(e: ControlEvent): + if self.handler is not None: + ce = e + if self.__result_converter is not None: + ce = self.__result_converter(e) + if ce is not None: + # ce.target = e.target + # ce.name = e.name + # ce.data = e.data + # ce.control = e.control + # ce.page = e.page + data = json.loads(e.data) + ce.files = data.get("files",[]) + + if ce is not None: + if asyncio.iscoroutinefunction(self.handler): + await self.handler(ce) + else: + e.page.run_thread(self.handler, ce) + + return fn + + +class DropZone(ConstrainedControl, AdaptiveControl): + def __init__( + self, + content: Optional[Control] = None, + + on_dropped: Optional[callable] = None, + on_exited: Optional[callable] = None, + on_entered: Optional[callable] = None, + + allowed_file_types: Optional[list] = [], + + + + clip_behavior: Optional[ClipBehavior] = None, + alignment: Optional[Alignment] = None, + fit: Optional[StackFit] = None, + + # + # ConstrainedControl + # + ref: Optional[Ref] = None, + key: Optional[str] = None, + width: OptionalNumber = None, + height: OptionalNumber = None, + left: OptionalNumber = None, + top: OptionalNumber = None, + right: OptionalNumber = None, + bottom: OptionalNumber = None, + expand: Union[None, bool, int] = None, + expand_loose: Optional[bool] = None, + col: Optional[ResponsiveNumber] = None, + opacity: OptionalNumber = None, + rotate: RotateValue = None, + scale: ScaleValue = None, + offset: OffsetValue = None, + aspect_ratio: OptionalNumber = None, + animate_opacity: Optional[AnimationValue] = None, + animate_size: Optional[AnimationValue] = None, + animate_position: Optional[AnimationValue] = None, + animate_rotation: Optional[AnimationValue] = None, + animate_scale: Optional[AnimationValue] = None, + animate_offset: Optional[AnimationValue] = None, + on_animation_end: OptionalControlEventCallable = None, + tooltip: TooltipValue = None, + badge: Optional[BadgeValue] = None, + visible: Optional[bool] = None, + disabled: Optional[bool] = None, + data: Any = None, + ): + ConstrainedControl.__init__( + self, + ref=ref, + key=key, + width=width, + height=height, + left=left, + top=top, + right=right, + bottom=bottom, + expand=expand, + expand_loose=expand_loose, + col=col, + opacity=opacity, + rotate=rotate, + scale=scale, + offset=offset, + aspect_ratio=aspect_ratio, + animate_opacity=animate_opacity, + animate_size=animate_size, + animate_position=animate_position, + animate_rotation=animate_rotation, + animate_scale=animate_scale, + animate_offset=animate_offset, + on_animation_end=on_animation_end, + tooltip=tooltip, + badge=badge, + visible=visible, + disabled=disabled, + data=data, + ) + + self.__on_dropped = EventHandler(lambda e: ListFiles(e)) + self._add_event_handler("dropped", self.__on_dropped.get_handler()) + + + self.content = content + self.clip_behavior = clip_behavior + self.alignment = alignment + self.fit = fit + + self.on_dropped = on_dropped + self.on_entered = on_entered + self.on_exited = on_exited + + self.allowed_file_types = allowed_file_types + + + def _get_control_name(self): + return "dropzone" + + def _get_children(self): + children = [] + if self.__content is not None: + self.__content._set_attr_internal("n", "content") + children.append(self.__content) + return children + + def before_update(self): + super().before_update() + self._set_attr_json("alignment", self.__alignment) + self._set_attr_json("allowedFileTypes", self.allowed_file_types) + self._set_attr_json("disabled", self.disabled) + + + # content + @property + def content(self) -> Optional[Control]: + return self.__content + + @content.setter + def content(self, value: Optional[Control]): + self.__content = value + + # clip_behavior + @property + def clip_behavior(self) -> Optional[ClipBehavior]: + return self.__clip_behavior + + @clip_behavior.setter + def clip_behavior(self, value: Optional[ClipBehavior]): + self.__clip_behavior = value + self._set_enum_attr("clipBehavior", value, ClipBehavior) + + # alignment + @property + def alignment(self) -> Optional[Alignment]: + return self.__alignment + + @alignment.setter + def alignment(self, value: Optional[Alignment]): + self.__alignment = value + + + # on_dropped + @property + def on_dropped(self) -> OptionalControlEventCallable: + return self.__on_dropped.handler + + @on_dropped.setter + def on_dropped(self, handler: OptionalControlEventCallable): + self.__on_dropped.handler = handler + + + # on_entered + @property + def on_entered(self) -> OptionalControlEventCallable: + return self._get_event_handler("entered") + + @on_entered.setter + def on_entered(self, handler: OptionalControlEventCallable): + self._add_event_handler("entered", handler) + + # on_exited + @property + def on_exited(self) -> OptionalControlEventCallable: + return self._get_event_handler("exited") + + @on_exited.setter + def on_exited(self, handler: OptionalControlEventCallable): + self._add_event_handler("exited", handler) + diff --git a/sdk/python/packages/flet/src/flet/core/layout_builder.py b/sdk/python/packages/flet/src/flet/core/layout_builder.py index 0835e7796..f0287c1e7 100644 --- a/sdk/python/packages/flet/src/flet/core/layout_builder.py +++ b/sdk/python/packages/flet/src/flet/core/layout_builder.py @@ -4,7 +4,8 @@ from flet.core.adaptive_control import AdaptiveControl from flet.core.alignment import Alignment -from flet.core.animation import AnimationValue +from flet.core.event_handler import EventHandler + from flet.core.constrained_control import ConstrainedControl from flet.core.control_event import ControlEvent from flet.core.control import Control, OptionalNumber @@ -30,8 +31,8 @@ def __init__(self, e: ControlEvent): d = json.loads(e.data) self.width: float = d.get("width") self.height: float = d.get("height") - self.x_pos: float = d.get("x_position") - self.y_pos: float = d.get("y_position") + self.x: float = d.get("xPos") + self.y: float = d.get("yPos") class LayoutBuilder(ConstrainedControl, AdaptiveControl): @@ -70,14 +71,8 @@ def __init__( self.clip_behavior = clip_behavior self.alignment = alignment self.fit = fit - self.__on_change_callback = on_change - - self.width_layout: float = None - self.height_layout: float = None - self.__update_size_on_init = update_size_on_init - self.on_change = self.__on_change @@ -85,8 +80,6 @@ def __on_change(self,e): e = LayoutDimensions(e) self.width = e.width self.height = e.height - self.x = e.x_pos - self.y = e.y_pos if self.__on_change_callback: self.__on_change_callback(e) @@ -104,6 +97,7 @@ def before_update(self): super().before_update() if self.__update_size_on_init==True: self._set_attr_json("update_on_build", self.__update_size_on_init) + print(f"update_on_build set: {self.__update_size_on_init}") self._set_attr_json("alignment", self.__alignment) # content @@ -143,3 +137,15 @@ def on_change(self) -> OptionalControlEventCallable: def on_change(self, handler: OptionalControlEventCallable): self._add_event_handler("layout_change", handler) + @property + def layout_size(self): + width = self._get_attr("layoutWidth") + height = self._get_attr("layoutHeight") + return (float(width),float(height)) + + + @property + def layout_pos(self): + x_pos = self._get_attr("xPos") + y_pos = self._get_attr("yPos") + return (float(x_pos),float(y_pos)) diff --git a/sdk/python/packages/flet/src/flet/core/page.py b/sdk/python/packages/flet/src/flet/core/page.py index 60239c49d..da8735c7a 100644 --- a/sdk/python/packages/flet/src/flet/core/page.py +++ b/sdk/python/packages/flet/src/flet/core/page.py @@ -178,6 +178,15 @@ def bgcolor(self) -> Optional[ColorValue]: def bgcolor(self, value: Optional[ColorValue]): self.__bgcolor = value self.page._set_enum_attr("windowBgcolor", value, ColorEnums) + + # bgblur + @property + def bgblur(self) -> Optional[str]: + return self.page._get_attr("windowBgblur"), + + @bgblur.setter + def bgblur(self, value: Optional[str]): + self.page._set_attr("windowBgblur", value) # width @property From 13177eda5246faa175f0b6267777a470a21b40a9 Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Thu, 28 Nov 2024 17:20:50 +0200 Subject: [PATCH 05/10] update layout_builder.py --- sdk/python/packages/flet/src/flet/core/layout_builder.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/python/packages/flet/src/flet/core/layout_builder.py b/sdk/python/packages/flet/src/flet/core/layout_builder.py index f0287c1e7..4a591211c 100644 --- a/sdk/python/packages/flet/src/flet/core/layout_builder.py +++ b/sdk/python/packages/flet/src/flet/core/layout_builder.py @@ -97,7 +97,6 @@ def before_update(self): super().before_update() if self.__update_size_on_init==True: self._set_attr_json("update_on_build", self.__update_size_on_init) - print(f"update_on_build set: {self.__update_size_on_init}") self._set_attr_json("alignment", self.__alignment) # content From 35d76e00268a793b051615a6a71913850b447cbc Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Thu, 28 Nov 2024 17:25:42 +0200 Subject: [PATCH 06/10] update layout_builder.dart and layout_builder.py --- packages/flet/lib/src/controls/layout_builder.dart | 4 ++-- sdk/python/packages/flet/src/flet/core/layout_builder.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/flet/lib/src/controls/layout_builder.dart b/packages/flet/lib/src/controls/layout_builder.dart index 1b314a453..e388be7ac 100644 --- a/packages/flet/lib/src/controls/layout_builder.dart +++ b/packages/flet/lib/src/controls/layout_builder.dart @@ -47,8 +47,8 @@ class _LayoutBuilderControlState extends State widget.backend.updateControlState(widget.control.id, {"layoutWidth": width.toString()}); widget.backend.updateControlState(widget.control.id, {"layoutheight": height.toString()}); - widget.backend.updateControlState(widget.control.id, {"xPos": width.toString()}); - widget.backend.updateControlState(widget.control.id, {"yPos": height.toString()}); + widget.backend.updateControlState(widget.control.id, {"x_position": xPosition.toString()}); + widget.backend.updateControlState(widget.control.id, {"y_position": yPosition.toString()}); widget.backend.triggerControlEvent( widget.control.id, diff --git a/sdk/python/packages/flet/src/flet/core/layout_builder.py b/sdk/python/packages/flet/src/flet/core/layout_builder.py index 4a591211c..68b24c2f8 100644 --- a/sdk/python/packages/flet/src/flet/core/layout_builder.py +++ b/sdk/python/packages/flet/src/flet/core/layout_builder.py @@ -31,8 +31,8 @@ def __init__(self, e: ControlEvent): d = json.loads(e.data) self.width: float = d.get("width") self.height: float = d.get("height") - self.x: float = d.get("xPos") - self.y: float = d.get("yPos") + self.x: float = d.get("x_position") + self.y: float = d.get("y_position") class LayoutBuilder(ConstrainedControl, AdaptiveControl): @@ -145,6 +145,6 @@ def layout_size(self): @property def layout_pos(self): - x_pos = self._get_attr("xPos") - y_pos = self._get_attr("yPos") + x_pos = self._get_attr("x_position") + y_pos = self._get_attr("y_position") return (float(x_pos),float(y_pos)) From 4f3156caa2878f72e248f2a27a4ee20b6c108d12 Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Thu, 28 Nov 2024 19:59:38 +0300 Subject: [PATCH 07/10] Update desktop.dart --- packages/flet/lib/src/utils/desktop.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flet/lib/src/utils/desktop.dart b/packages/flet/lib/src/utils/desktop.dart index 11fad15fb..eddd23495 100644 --- a/packages/flet/lib/src/utils/desktop.dart +++ b/packages/flet/lib/src/utils/desktop.dart @@ -22,7 +22,7 @@ Future setWindowBackgroundColor(Color bgcolor) async { } Future setWindowBackgroundBlur(String bgblur) async { - if (isDesktop()) { + if (isDesktopPlatform()) { debugPrint("setWindowBackgroundBlur($bgblur)"); await setWindowEffect(bgblur); } @@ -309,4 +309,4 @@ Future getWindowMediaData() async { } else { return Future.value(m); } -} \ No newline at end of file +} From de2d87ecac8b79b4d830548f1510210e5fde2a28 Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Thu, 28 Nov 2024 23:17:16 +0200 Subject: [PATCH 08/10] update 9 files --- client/pubspec.lock | 24 ++++++++++++------------ packages/flet_dropzone/pubspec.yaml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/pubspec.lock b/client/pubspec.lock index bdaedae2e..2d6430b35 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -255,28 +255,28 @@ packages: path: "../packages/flet" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_ads: dependency: "direct main" description: path: "../packages/flet_ads" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_audio: dependency: "direct main" description: path: "../packages/flet_audio" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_audio_recorder: dependency: "direct main" description: path: "../packages/flet_audio_recorder" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_dropzone: dependency: "direct main" description: @@ -290,56 +290,56 @@ packages: path: "../packages/flet_flashlight" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_geolocator: dependency: "direct main" description: path: "../packages/flet_geolocator" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_lottie: dependency: "direct main" description: path: "../packages/flet_lottie" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_map: dependency: "direct main" description: path: "../packages/flet_map" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_permission_handler: dependency: "direct main" description: path: "../packages/flet_permission_handler" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_rive: dependency: "direct main" description: path: "../packages/flet_rive" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_video: dependency: "direct main" description: path: "../packages/flet_video" relative: true source: path - version: "0.24.1" + version: "0.25.0" flet_webview: dependency: "direct main" description: path: "../packages/flet_webview" relative: true source: path - version: "0.24.1" + version: "0.25.0" flutter: dependency: "direct main" description: flutter diff --git a/packages/flet_dropzone/pubspec.yaml b/packages/flet_dropzone/pubspec.yaml index 4010c8cc9..3f40de12e 100644 --- a/packages/flet_dropzone/pubspec.yaml +++ b/packages/flet_dropzone/pubspec.yaml @@ -2,7 +2,7 @@ name: flet_dropzone description: Flet Drop Zone control homepage: https://flet.dev repository: https://github.com/flet-dev/flet/packages/flet_dropzone -version: 0.24.1 +version: 0.26.0 environment: sdk: '>=3.2.3 <4.0.0' From af29c53e63adab84acb63423b682a99b6bc4047e Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Fri, 29 Nov 2024 04:01:15 +0200 Subject: [PATCH 09/10] chore: update pubspec.yaml and pubspec.yaml --- packages/flet_dropzone/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flet_dropzone/pubspec.yaml b/packages/flet_dropzone/pubspec.yaml index 3f40de12e..4010c8cc9 100644 --- a/packages/flet_dropzone/pubspec.yaml +++ b/packages/flet_dropzone/pubspec.yaml @@ -2,7 +2,7 @@ name: flet_dropzone description: Flet Drop Zone control homepage: https://flet.dev repository: https://github.com/flet-dev/flet/packages/flet_dropzone -version: 0.26.0 +version: 0.24.1 environment: sdk: '>=3.2.3 <4.0.0' From d3efd7a24a904be38108734fc002f8f6d5a429b7 Mon Sep 17 00:00:00 2001 From: Wanna-Pizza Date: Fri, 13 Dec 2024 20:50:46 +0200 Subject: [PATCH 10/10] Correcting LayoutBuilder --- client/pubspec.lock | 2 +- .../flet/lib/src/controls/layout_builder.dart | 68 +++++------ .../src/controls/layout_builder_backup.dart | 110 ++++++++++++++++++ packages/flet_dropzone/pubspec.yaml | 2 +- .../flet/src/flet/core/layout_builder.py | 21 ++-- sdk/python/packages/flet/src/flet/version.py | 2 +- 6 files changed, 148 insertions(+), 57 deletions(-) create mode 100644 packages/flet/lib/src/controls/layout_builder_backup.dart diff --git a/client/pubspec.lock b/client/pubspec.lock index cccfbae90..153c23a6d 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -283,7 +283,7 @@ packages: path: "../packages/flet_dropzone" relative: true source: path - version: "0.25.1" + version: "0.24.1" flet_flashlight: dependency: "direct main" description: diff --git a/packages/flet/lib/src/controls/layout_builder.dart b/packages/flet/lib/src/controls/layout_builder.dart index e388be7ac..e339714bc 100644 --- a/packages/flet/lib/src/controls/layout_builder.dart +++ b/packages/flet/lib/src/controls/layout_builder.dart @@ -27,14 +27,12 @@ class LayoutBuilderControl extends StatefulWidget { @override State createState() => _LayoutBuilderControlState(); } + class _LayoutBuilderControlState extends State with FletStoreMixin { - final GlobalKey _widgetKey = GlobalKey(); Size? _lastSize; bool _hasInitialized = false; bool _updateOnBuild = false; - double xPosition = 0.0; - double yPosition = 0.0; @override void initState() { @@ -43,24 +41,21 @@ class _LayoutBuilderControlState extends State } void onChange(double width, double height) { - - widget.backend.updateControlState(widget.control.id, {"layoutWidth": width.toString()}); - widget.backend.updateControlState(widget.control.id, {"layoutheight": height.toString()}); + widget.backend.updateControlState(widget.control.id, {"layoutWidth": width.toString()}); + widget.backend.updateControlState(widget.control.id, {"layoutHeight": height.toString()}); + + widget.backend.triggerControlEvent( + widget.control.id, + "layout_change", + jsonEncode({ + "height": height, + "width": width, + }), + ); + + setState(() {}); +} - widget.backend.updateControlState(widget.control.id, {"x_position": xPosition.toString()}); - widget.backend.updateControlState(widget.control.id, {"y_position": yPosition.toString()}); - - widget.backend.triggerControlEvent( - widget.control.id, - "layout_change", - jsonEncode({ - "height": height, - "width": width, - "x_position": xPosition, - "y_position": yPosition, - }), - ); - } @override Widget build(BuildContext context) { @@ -77,22 +72,10 @@ class _LayoutBuilderControlState extends State ? createControl(widget.control, contentCtrls.first.id, disabled, parentAdaptive: adaptive) : null; - - return constrainedControl( - context, - LayoutBuilder( + + var layoyt_ = LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { WidgetsBinding.instance.addPostFrameCallback((_) { - if (_widgetKey.currentContext != null) { - final RenderBox box = - _widgetKey.currentContext!.findRenderObject() as RenderBox; - final Offset position = box.localToGlobal(Offset.zero); - xPosition = position.dx; - yPosition = position.dy; - - debugPrint("Widget position: x: $xPosition, y: $yPosition"); - } - final Size currentSize = Size(constraints.maxWidth, constraints.maxHeight); if (_hasInitialized == false && _updateOnBuild == true) { @@ -108,15 +91,18 @@ class _LayoutBuilderControlState extends State "LayoutBuilder dimensions: Width: ${constraints.maxWidth}, Height: ${constraints.maxHeight}"); }); - return Container( - key: _widgetKey, + return Container( clipBehavior: Clip.none, - alignment: parseAlignment(widget.control, "alignment") ?? - AlignmentDirectional.topStart, - child: child, - ); + alignment: parseAlignment(widget.control, "alignment") ?? AlignmentDirectional.topStart, + child: child + ); + }, - ), + ); + + return constrainedControl( + context, + layoyt_, widget.parent, widget.control, ); diff --git a/packages/flet/lib/src/controls/layout_builder_backup.dart b/packages/flet/lib/src/controls/layout_builder_backup.dart new file mode 100644 index 000000000..e339714bc --- /dev/null +++ b/packages/flet/lib/src/controls/layout_builder_backup.dart @@ -0,0 +1,110 @@ +import 'package:flutter/widgets.dart'; +import '../models/control.dart'; +import '../utils/alignment.dart'; +import 'create_control.dart'; +import '../flet_control_backend.dart'; +import 'flet_store_mixin.dart'; +import 'dart:convert'; + +class LayoutBuilderControl extends StatefulWidget { + final Control? parent; + final Control control; + final bool parentDisabled; + final bool? parentAdaptive; + final List children; + final FletControlBackend backend; + + const LayoutBuilderControl({ + super.key, + this.parent, + required this.control, + required this.children, + required this.parentDisabled, + required this.parentAdaptive, + required this.backend, + }); + + @override + State createState() => _LayoutBuilderControlState(); +} + +class _LayoutBuilderControlState extends State + with FletStoreMixin { + Size? _lastSize; + bool _hasInitialized = false; + bool _updateOnBuild = false; + + @override + void initState() { + super.initState(); + _updateOnBuild = widget.control.attrBool("update_on_build") ?? false; + } + + void onChange(double width, double height) { + widget.backend.updateControlState(widget.control.id, {"layoutWidth": width.toString()}); + widget.backend.updateControlState(widget.control.id, {"layoutHeight": height.toString()}); + + widget.backend.triggerControlEvent( + widget.control.id, + "layout_change", + jsonEncode({ + "height": height, + "width": width, + }), + ); + + setState(() {}); +} + + + @override + Widget build(BuildContext context) { + debugPrint("Stack with layout builder build: ${widget.control.id}"); + + bool disabled = widget.control.isDisabled || widget.parentDisabled; + bool? adaptive = + widget.control.attrBool("adaptive") ?? widget.parentAdaptive; + + var contentCtrls = + widget.children.where((c) => c.name == "content" && c.isVisible); + + Widget? child = contentCtrls.isNotEmpty + ? createControl(widget.control, contentCtrls.first.id, disabled, + parentAdaptive: adaptive) + : null; + + var layoyt_ = LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + WidgetsBinding.instance.addPostFrameCallback((_) { + final Size currentSize = + Size(constraints.maxWidth, constraints.maxHeight); + if (_hasInitialized == false && _updateOnBuild == true) { + onChange(constraints.maxWidth, constraints.maxHeight); + } + if (_hasInitialized == true && _lastSize != currentSize) { + onChange(constraints.maxWidth, constraints.maxHeight); + } + _hasInitialized = true; + + _lastSize = currentSize; + debugPrint( + "LayoutBuilder dimensions: Width: ${constraints.maxWidth}, Height: ${constraints.maxHeight}"); + }); + + return Container( + clipBehavior: Clip.none, + alignment: parseAlignment(widget.control, "alignment") ?? AlignmentDirectional.topStart, + child: child + ); + + }, + ); + + return constrainedControl( + context, + layoyt_, + widget.parent, + widget.control, + ); + } +} diff --git a/packages/flet_dropzone/pubspec.yaml b/packages/flet_dropzone/pubspec.yaml index 4010c8cc9..f2a9cc3e9 100644 --- a/packages/flet_dropzone/pubspec.yaml +++ b/packages/flet_dropzone/pubspec.yaml @@ -2,7 +2,7 @@ name: flet_dropzone description: Flet Drop Zone control homepage: https://flet.dev repository: https://github.com/flet-dev/flet/packages/flet_dropzone -version: 0.24.1 +version: 0.25.1 environment: sdk: '>=3.2.3 <4.0.0' diff --git a/sdk/python/packages/flet/src/flet/core/layout_builder.py b/sdk/python/packages/flet/src/flet/core/layout_builder.py index 68b24c2f8..8e82c5866 100644 --- a/sdk/python/packages/flet/src/flet/core/layout_builder.py +++ b/sdk/python/packages/flet/src/flet/core/layout_builder.py @@ -31,8 +31,6 @@ def __init__(self, e: ControlEvent): d = json.loads(e.data) self.width: float = d.get("width") self.height: float = d.get("height") - self.x: float = d.get("x_position") - self.y: float = d.get("y_position") class LayoutBuilder(ConstrainedControl, AdaptiveControl): @@ -73,15 +71,18 @@ def __init__( self.fit = fit self.__on_change_callback = on_change self.__update_size_on_init = update_size_on_init + self.__old_width = None + self.__old_height = None self.on_change = self.__on_change def __on_change(self,e): e = LayoutDimensions(e) - self.width = e.width - self.height = e.height - if self.__on_change_callback: - self.__on_change_callback(e) + if e.width!=self.__old_width or e.height!=self.__old_height: + self.__old_height = e.height + self.__old_width = e.width + if self.__on_change_callback: + self.__on_change_callback(e) def _get_control_name(self): return "layoutbuilder" @@ -141,10 +142,4 @@ def layout_size(self): width = self._get_attr("layoutWidth") height = self._get_attr("layoutHeight") return (float(width),float(height)) - - - @property - def layout_pos(self): - x_pos = self._get_attr("x_position") - y_pos = self._get_attr("y_position") - return (float(x_pos),float(y_pos)) + \ No newline at end of file diff --git a/sdk/python/packages/flet/src/flet/version.py b/sdk/python/packages/flet/src/flet/version.py index f3a8f0a4d..4db4e6d4a 100644 --- a/sdk/python/packages/flet/src/flet/version.py +++ b/sdk/python/packages/flet/src/flet/version.py @@ -8,7 +8,7 @@ from flet.utils import is_mobile, is_windows, which # will be replaced by CI -version = "" +version = "0.25.1" def update_version():